[Tutor] Declaration order of classes... why it is important?

Dave Angel davea at ieee.org
Fri Aug 28 18:24:22 CEST 2009


Mac Ryan wrote:
> On Fri, 2009-08-28 at 08:55 -0400, Dave Angel wrote:
>   
>> Mac Ryan wrote:
>>     
>>> On Wed, 2009-08-26 at 21:32 -0400, Dave Angel wrote:
>>>
>>>   
>>>       
>>>> Now there are a couple of decorators that are in the standard library 
>>>> that everyone should know about:    classmethod() and staticmethod().  
>>>> They wrap a method in a new one (which ends up having the same name), 
>>>> such that the first argument is either eaten (staticmethod), or changed 
>>>> to a class (classmethod).
>>>>
>>>> Hope that was sufficient detail.
>>>>
>>>> DaveA
>>>>     
>>>>         
>>> Thank you all for your answer, I read the link posted by Kent end the
>>> explanations given by others, and now almost all the pieces of the
>>> puzzle are falling to their place.
>>>
>>> The (last?) thing still escaping my understanding is the difference
>>> between classmethod() and staticmethod(). I understand that both of them
>>> are to make sure a method will be associated with the class rather than
>>> with an instance of it (a bit like variables declared in a class but
>>> outside a method), so I assume I should use them like:
>>>
>>> class myClass(object):
>>>
>>>   @staticmethod / @classmethod
>>>   def method_of_class(self):
>>>     pass
>>>
>>>   def method_of_instances(self):
>>>     pass
>>>
>>> I am not sure I understood the difference between staticmethod end
>>> classmethod, though, even if I can guess it has to do with subclassing,
>>> (given classmethod go fish for the class name)... am I on the right
>>> track? Any hint (or full-fledged explanation!)? :)
>>>
>>> [The official documentation is a bit obscure for me as it refers to C#
>>> and Java, languages that I do not know.]
>>>
>>> Also, what is the advantage of using a method at class level rather than
>>> using it at object instance (I can imagine you can save some memory by
>>> not duplicating X times the same code, maybe... but what kind of designs
>>> require/call for use of statc/classmethods?)
>>>
>>> Thank you in advance for your help,
>>> Mac.
>>>
>>>
>>>   
>>>       
>> I thought someone (me) had already posted examples.  The method itself 
>> looks different in the three cases:
>>
>> class  MyClass(object):
>>
>>      def my_method1(self, arg):
>>               ... do something with this particular object
>>
>>      @classmethod
>>       def my_class_method(cls, arg)
>>                .... do something that may requires use of class, but 
>> that doesn't need to know the particular object
>>
>>       @staticmethod
>>        def my_static_method(arg):
>>                ....  do something which isn't affected at all by what 
>> class it's in
>>
>> self and cls parameters are named that merely by convention.  Unlike the 
>> "this" of C++, C#, and Java, these names have no specific meaning to the 
>> compiler. But convention is a very good thing, especially in this case.
>>
>> So the first difference between static and class is that the method 
>> signature must be different.  There's an extra parameter on the latter 
>> that must be included.  The second difference is that a classmethod may 
>> call other methods of the same class, and gets the appropriate methods 
>> even if the class was subclassed.
>>
>> The hint to play with is that any of these 3 may be called with an 
>> object  (obj.my_class_method()), and the last two may be called with a 
>> classname.
>>
>> DaveA
>>     
>
> Thank you Dave, very helpful as ever. I made a few tests and experiments
> to understand better, and I feel I am understanding how it works.
> Although the mechanic of this is increasingly clear, the logic behind is
> still a bit obscure: here comes a silly test on which results I have
> questions about
>
> ======================================================
>
> class A(object):
>   variable = 0
>   @classmethod
>   def cm(cls):
>     cls.variable += 3
>     
> class B(A):
>   pass
>   
> class C(B):
>   pass
>   
> a = A()
> b = B()
> c = C()
> print A.variable, a.variable
> print B.variable, b.variable
> print C.variable, c.variable
> print '---'
> a.cm()
> print A.variable, a.variable
> print B.variable, b.variable
> print C.variable, c.variable
> print '---'
> b.cm()
> print A.variable, a.variable
> print B.variable, b.variable
> print C.variable, c.variable
> print '---'
> c.cm()
> print A.variable, a.variable
> print B.variable, b.variable
> print C.variable, c.variable
> print '---'
> a.variable = 7
> A.variable = 4
> print A.variable, a.variable
> print B.variable, b.variable
> print C.variable, c.variable
> print '---'
> B.variable = 'x'
> print A.variable, a.variable
> print B.variable, b.variable
> print C.variable, c.variable
>
> ======================================================
>
> Now, the output of this test is as follows (I manually added the numbers
> on the right for reference in the remaining of the post):
>
> 0 0 # 1
> 0 0
> 0 0
> ---
> 3 3 # 2
> 3 3
> 3 3
> ---
> 3 3 # 3
> 6 6
> 6 6
> ---
> 3 3 # 4
> 6 6
> 9 9
> ---
> 4 7 # 5 
> 6 6
> 9 9
> ---
> 4 7 # 6
> x x
> 9 9
>
> #1 is plain obvious.
>
> #2 came as a bit of surprise: "Ah-ah!... so the class variable is the
> same for all the hierarchy of classes! I would have guessed each class
> had its own variable..."
>
> #3 and #4 are bigger surprises: "Uh? let me understand better, so... it
> must be like branches... as soon as I say that B.variable is different
> than its ancestors, all the descendants of B get updated... Yet, it's
> strange: this very thread on the mailing list has started with me asking
> about the order of declaration for classes, and I have been told that
> class variables are processed as soon as the interpreter hits the class
> definition... so I would have expected that C.variable would have been
> initialised already and would not dynamically change when B.variable
> does..."
>
> #5 confused me: it seems that a.variable can refer to both the class
> variable or a newly created object property... that sounds like a
> never-ending source of bugs to me: it is me who has then to remember if
> a given syntax refers to a class variable or to an object method?
>
> #6 really make no sense to me: it seems that the class variable of C has
> not been changed this time (as it did in #3)...
>
> The points above confuse me even more about the "design purposes" of
> classmethods and staticmethods...
>
> Any help/feedback, very much appreciated.
>
> mac.
>
>
>   
Great questions.  I love a person who is organized as you are.  And as 
willing to learn.

#3 and #4 - First let me point out that these are not class variables, 
but class attributes.   It's unfortunate that you called the attributes 
with the label 'variable'.  I frequently make the same mistake (call it 
sloppy mistake), but it might matter somewhere in the following 
discussion.  A class variable is simply a "variable" which references 
the class.  Think of it as an alias for the class.  Just like a list 
variable is a variable which references a list.  If you do:
     newclass = A

then you can use newclass in exactly the same way as A.     a = 
newclass(),  print newclass.variable, etc.

A class attribute, on the other hand, is what other languages would call 
a field of the class.  And it definitely does get initialized when the 
class is executed.  That's important in the earlier discussion because 
the *right-hand-side* has to be valid at that time, which is why you had 
the forward referencing problem.  So in the following code:

n = 12
class  A(object):
     attr = n

n = 42


the attribute A.attr is a reference to 12, not 42.  The confusion in 
this #3 and #4 case is that when some later expression tries to use a 
particular class attribute there are some rules for lookup.  I don't 
know the correct terminology, but I think of this as the "." operator.  
When I say  C.variable, the first search is in the classes dictionary.  
If it's not found there, then it searches in the parent class' 
dictionary, and so on.  The attribute is not copied, there's just a 
search order when it's accessed.

#5 - It's search order again (on what I call the dot operator).  When 
you reference  c.variable, it looks first in the dictionary for the 
instance object c, and then if it doesn't find it, it looks in the 
parent, which is the class.  And if it doesn't find it there, it again 
looks in the parent, and so on.

#6 - by this time, there is a class attribute C.variable, so changing 
B.variable has no effect    Probably the key thing that misleads people 
is that the += operator may actually create a new attribute.  That 
happened when you called  b.cm() and c.cm().

You might be helped by using help(B) before and after the call to 
b.cm().  It says a lot that you might not care about, but it makes clear 
what attribute are "defined here" versus "inherited from..."


HTH
DaveA


More information about the Tutor mailing list