Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

calling classmethod using 'self'?

class Hello:
    def load(self):
        self.open('World')
    @classmethod
    def open(cls,print_string):
        print 'Hello ' + print_string


class Hello:
    def load(self):
        Hello.open('World')
    @classmethod
    def open(cls,print_string):
        print 'Hello ' + print_string

I am finding difficult to understand from above two classes different style of calling class method. One uses self and another class name while calling, when should one use first and when should the second be used?

Good explanation will really clarify @classmethod concept.

like image 747
Pravin Agre Avatar asked May 28 '17 15:05

Pravin Agre


1 Answers

The issue at heart here really isn't classmethod, it is how Python handles attributes. There are two different kinds of attributes, class-level attributes (in other languages sometimes called "static" attributes) and instance attributes.

Consider:

>>> class A:
...    a = 'foo'
...    def __init__(self):
...       self.x = 42
...    def method(self):
...       print(A.a)
...       print(self.a)
...       print(self.x)
...
>>> a = A()
>>> a.method()
foo
foo
42

Now, in Python, class-level attributes (which includes methods themselves) belong to the class namespace, accessible via the __dict__ attribute:

>>> from pprint import pprint
>>> pprint(A.__dict__)
mappingproxy({'__dict__': <attribute '__dict__' of 'A' objects>,
              '__doc__': None,
              '__init__': <function A.__init__ at 0x101589d90>,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'A' objects>,
              'a': 'foo',
              'method': <function A.method at 0x10863a620>})

The instance attributes are only those you explicitly assign to an instance using self.something = somethingsomething:

>>> pprint(a.__dict__)
{'x': 42}

However, when you access an attribute, you can think that Python first checks the __dict__ of the instance. If it doesn't find it, it then checks the namespace of the class. If it still doesn't find it, it checks the namespaces of any parent-classes in the method-resolution order (i.e. inheritance).

So, classes being simply other objects themselves, if you do:

Hello.open('World')

This checks the Hello objects namespace and finds it. Similarly, if you use the method on the instance,

self.open('World')

It checks the instances namespace, doesn't find it, then checks the classes namespace and does. Now, if you don't expect to extend the class, then there really isn't a difference. But if you do then the semantics will change. If you derive from Hello, and override open and not load, the Hello.open will always call the super-classes open method. However, the self.open version will find it in the classes namespace, and never check the parent classes. So, depending on exactly the behavior you want, you can use either, but usually if you class is designed correctly then self is the way to go. But maybe not. So, to give you a concrete example:

>>> class B(A):
...    a = 'bar'
...
>>> b = B()
>>> b.method()
foo
bar
42
>>>
like image 116
juanpa.arrivillaga Avatar answered Oct 30 '22 01:10

juanpa.arrivillaga