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
_______________________________________________
Tutor maillist  -  [email protected]
http://mail.python.org/mailman/listinfo/tutor

Reply via email to