I come from java background, so I am slightly confused here.
Consider the code snippet below:
class A():
def __init__(self, **kwargs):
self.obj_var = "I am obj var"
@classmethod
def class_method(cls):
print cls.obj_var # this line is in question here
cls.cls_obj = "I m class object"
return cls.cls_obj
This throws an error :
In [30]: a = A()
In [31]: a.obj_var
Out[31]: 'I am obj var'
In [32]: a.class_method()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-32-3dcd9d512548> in <module>()
----> 1 a.class_method()
<ipython-input-29-9c0d341ad75f> in class_method(cls)
8 @classmethod
9 def class_method(cls):
---> 10 print cls.obj_var
11 cls.cls_obj = "I m class object"
12 return cls.cls_obj
AttributeError: class A has no attribute 'obj_var'
iIf I comment out @classmethod
, this will work fine.
My understanding (which is incorrect m sure) :
When I do a = A()
, then all the variables(obj_var
) created in __init__
can be accessed via this a
when passed to any other classmethod
of same class.Apparently this is not the case.
Question(s)
why am i not able to access __init__
var
s in class_method
when decorater @classmethod
is mentioned in the method but on removing the decorater, it works fine?
how python internally process this particular class upon compilation?
is there any way i can use @classmethod
and the __init__
vars in same method?
Let's talk about how Python's methods actually work.
You may have noticed that Python methods are declared just like free-standing functions, but inside a class. That's because Python methods really are free-standing functions that happen to be inside a class. The self
/cls
argument is not special. It's just the first argument of the function.
Before we go any further, I'd like to point out that you don't appear to be explicitly inheriting from object
. If you are working in Python 2.x, there is no object
at the root of the graph unless you explicitly inherit from it. That is a bad thing, and you should inherit directly or indirectly from object
whenever possible in new code. Inheriting from object
in Python 3 is legal and harmless, but unnecessary. The rest of this discussion assumes that you are either working in 3.x or have fixed this.
When you access a variable, function, method, or any other type of object with foo.bar
, certain hooks known as the "descriptor protocol" get invoked. You don't have to know the details of this to understand how functions work. All you have to know is this:
bar
directly attached to foo
(and foo
is not a class), we just return it directly.(*)foo
is a class and bar
is a @classmethod
function(**) declared either in foo
or in one of its superclasses, then the first argument is set to foo
before we return it. Otherwise, it is returned unchanged.(***) If we returned something, we stop here.foo
. This consists of foo
's class (known as type(foo)
), that class's superclass, and so on until we get to object
. In cases of multiple inheritance, this gets a little more complicated, but again, you don't need to know that.bar
variable from the first class which has one (call it Baz
).Baz.bar
is a regular, undecorated function, set its first argument to foo
and return it.Baz.bar
is a @classmethod
function, set its first argument to type(foo)
and return it.Baz.bar
is a @staticmethod
function, or not a function(**) at all, return it unchanged.As you can see, if the method is declared @classmethod
, the first argument is always the class, and never the instance, regardless of how the function is invoked. That means you don't have access to the instance variables of foo
, since you don't have access to foo
itself. Any variables set in __init__()
are instance variables, so they are not visible here.
And here are all the things I lied about:
(*): Python actually does the rest of this work first and then comes back to this step. But that only matters for things like @property
, which can actually override local instance variables. Regular methods cannot.
(**): This is a lie. Python also does special processing on @property
functions in this case, and on anything else that implements certain special methods.
(***): I am also ignoring unbound methods in Python 2.x because, aside from certain really weird cases (when you try to pass an object of the wrong type as the first argument by hand), they make no difference whatsoever. They do not exist in Python 3.