Python Forum
What is the difference between them in Python 3.7?
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
What is the difference between them in Python 3.7?
#1
Hello.

I've just wondered if there were a difference between them.

class cls_a:
    def func(arg):
        print(arg)

class cls_b:
    @staticmethod
    def func(arg):
        print(arg)
Both give same result as instance = cls_x.func('foo'); however, the first one seems a sort of buggy stuff.
Is there any reasons they act like same stuff?

Thanks.
Reply
#2
The difference appears when you call the method through an instance of the class
>>> a = cls_a()
>>> b = cls_b()
>>> a.func()
<__main__.cls_a object at 0x7f9e74843240>
>>> b.func()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func() missing 1 required positional argument: 'arg'
>>> b.func('foo')
foo
in the case of cls_a, the instance a is passed to func() as the first argument, while in the case of a staticmethod, the instance is not passed. The @staticmethod decorator could perhaps be renamed @dontpassinstance.
Reply
#3
Thanks for replying, Mr.Gribouillis!

I guessed static method is supposed to be used like a "function". Let's say:

>>> cls_b.func("foo")
foo
>>> 
Nevertheless, cls_a.func("foo") gives me the same result, and I was so confused.

>>> cls_a.func("foo")
foo
>>> 
As you said, the instance a MUST be passed to func() as the first argument, but the result seems ignoring the fact.
Is there any meaning for it? Implementation convenience?
Reply
#4
The class cls_a is different from an instance cls_a()
Reply
#5
Classnames should be nouns like "Person", "User", "Connector", etc.
It should help the reader to differ between classes, objects, functions.

The functions classmethod, staticmethod and property are decorators.
A decorator is a function, which takes another function and returns the value of the taken function.

The simplest possible closure, which can used as decorator:
def wrapper(func):
    def inner():
        result = func()
        return result
    return inner


def foo():
    return 42


wrapped_foo = wrapper(foo)
The function wrapper takes as argument the function.
Inside the function a inner function is created,
which calls the nonlocal func.
Nonlocal means, that the name is not in globals nor in locals.
In simple word: The function inner has access to the arguments of wrapper.
The wrapper returns the function inner, but it is not called.
wrapped_foo is now the function inner.
When the function wrapped_foo is called, the function inner
returns the result from func (in this case the function foo).

Instead of calling the wrapper with the function, we got syntactical sugar for it:
@wrapper
def foo():
    return 42

# is the same like
foo = wrapper(foo)
This gives us the ability to change functions without changing the code inside of a function.
I helps to change the behavior of a function.
This is where the built-in functions classmethod, staticmethod and property comes in place.

This functions changes the behavior of methods:
  • classmethod: The first argument is the class-object itself
  • staticmethod: No first argument of class or instance. It's a pure function without any access to the class or instance.
  • property: Converts a method into an attribute. This is often used, when you want to protect attributes against overwriting. Also assigning can be controlled. For example a class needs to store a temperature in Kelvin, but the input is in Celsius.

Here an example class:
class A:
    def normal_method(self):
        print(self)
    @classmethod
    def class_method(cls):
        print(cls)
    @staticmethod
    def static_method():
        print('Static Method')
    @property
    def prop(self):
        return 42
    @prop.setter
    def prop(self, value):
        return f'Set to {value}'
Methods:
Output:
Method: class_method <bound method A.class_method of <class '__main__.A'>> Method: normal_method <function A.normal_method at 0x7fa22539c620> Method: prop <property object at 0x7fa225390ae8> Method: static_method <function A.static_method at 0x7fa225cfd0d0>
Bound method means, that the first argument of the function is a class or instance, which is implicit.
The name self for instances and cls for class is a naming convention in Python.
It's possible to call them this like in JavaScript. But don't do this.
Just play a little bit around with classes to learn this tricks.


PS: In my text I wrote sometimes functions instead of methods. Technically a method is function, but in oop sense we call it method. Another addition: Everything in Python is an object, even classes, functions, exceptions, datatypes. Names are pointing to the objects. An object can have more than one name. When I say function wrapped_function, I mean the name wrapped_function which points to the function, which lives in your memory.
Almost dead, but too lazy to die: https://sourceserver.info
All humans together. We don't need politicians!
Reply
#6
Thanks, guys!

The problem occurs "when I don't use an instance". I know it is not an ordinal case. Just wondered "what the hell is going on?" inside.

Let's say, the "expected" way of writing a code would be like this.

class cls_c:
    def func(self, arg):
        print(arg)
The normal usage would be like this:

>>> inst = cls_c()
>>> inst.func('foo')
foo
>>> 
However, we can use it like this:

# BAD Way
>>> cls_c.func("foo")
Traceback (most recent call last):
  File "<pyshell#0>", line 1, in <module>
    cls_c.func("foo")
TypeError: func() missing 1 required positional argument: 'arg'
# Expected way
>>> cls_c.func(cls_c, "foo")
foo
>>> 
The second one shows what "self" .... actually "the first argument of a method" does. In this way, we cannot rely on something "implicit"; thus, we have to give a concrete "class name", in this case, "cls_c" for the first argument.
This is reasonable, and I've found this kind of examples a lot on the internet. A lot of people give this kind of example.
Therefore, the problem is cls_a. This gives me a headache.

class cls_a:
    def func(arg):
        print(arg)
I've expected this would give an error. But NOT.

>>> cls_a.func('foo')
foo
>>> 
In this case, theoretically, "arg" should be the class name itself; however, it works as an ordinally argument. Strange. Why? At least, the "rule of first argument of a class" seems to be broken here.
Is this Python's specification?
Reply
#7
Quote:In this case, theoretically, "arg" should be the class name itself; however, it works as an ordinally argument. Strange. Why? At least, the "rule of first argument of a class" seems to be broken here.
Is this Python's specification?

If I remind right, it was very easy to implement classes in one day into the Python language.
Later it turned out that this kind of implementation opens up further possibilities.

Other languages don't have this feature. So they can't do, what Python is able to do.
Almost dead, but too lazy to die: https://sourceserver.info
All humans together. We don't need politicians!
Reply
#8
@cametan you can implement a decorator behaving the way you want by using the descriptor protocol. See the spam decorator below
>>> from functools import partial
>>> class spam:
...     def __init__(self, func):
...         self._func = func
...     def __get__(self, instance, owner):
...         return partial(self._func, owner if instance is None else instance)
... 
>>> class cls_a:
...     @spam
...     def func(arg):
...         print(arg)
... 
>>> cls_a.func()
<class '__main__.cls_a'>
>>> cls_a().func()
<__main__.cls_a object at 0x7fee7074aac8>
In fact, I once wrote a better @mixedmethod decorator which works with two implicit arguments for each method.
Reply
#9
Now it is comfortable for me if this behavior is expected and guaranteed in Python.

Thanks, guys!
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  how to find difference between fake driving license or real using python? pjaymn 2 2,500 Dec-20-2020, 08:41 PM
Last Post: jefsummers
  python 3 find difference between 2 files pd007 2 2,082 May-22-2020, 01:16 AM
Last Post: Larz60+
  Python Script to Produce Difference Between Files and Resolve DNS Query for the Outpu sultan 2 2,463 May-22-2019, 07:20 AM
Last Post: buran
  Socket creation speed difference Python 2.7 / Python 3.7 PiAil 1 2,426 Feb-13-2019, 01:55 PM
Last Post: PiAil
  python how to find difference between two values hare 1 6,421 Jan-14-2019, 10:18 PM
Last Post: Gribouillis
  1 What is Jupyter? and what's the difference between it and "pure python"? InigoSJ 2 2,875 Apr-05-2018, 07:24 PM
Last Post: InigoSJ

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020