Take the following example script:
class A(object): @classmethod def one(cls): print("I am class") @staticmethod def two(): print("I am static") class B(object): one = A.one two = A.two B.one() B.two()
When I run this script with Python 2.7.11 I get:
I am class Traceback (most recent call last): File "test.py", line 17, in <module> B.two() TypeError: unbound method two() must be called with B instance as first argument (got nothing instead)
It appears that the @classmethod decorator is preserved across the classes, but @staticmethod is not.
Python 3.4 behaves as expected:
I am class I am static
Why does Python2 not preserve @staticmethod, and is there a workaround?
edit: taking two out of a class (and retaining @staticmethod) seems to work, but this still seems strange to me.
@staticmethod function is nothing more than a function defined inside a class. It is callable without instantiating the class first. It's definition is immutable via inheritance. @classmethod function also callable without instantiating the class, but its definition follows Sub class, not Parent class, via inheritance.
The static method does not take any specific parameter. Class method can access and modify the class state. Static Method cannot access or modify the class state. The class method takes the class as parameter to know about the state of that class.
In Python, the @classmethod decorator is used to declare a method in the class as a class method that can be called using ClassName. MethodName() . The class method can also be called using an object of the class. The @classmethod is an alternative of the classmethod() function.
The @staticmethod is a built-in decorator that defines a static method in the class in Python. A static method doesn't receive any reference argument whether it is called by an instance of a class or by the class itself.
classmethod
and staticmethod
are descriptors, and neither of them are doing what you expect, not just staticmethod
.
When you access A.one
, it's creating a bound method on A
, then making that an attribute of B
, but because it's bound to A
, the cls
argument will always be A
, even if you call B.one
(this is the case on both Python 2 and Python 3; it's wrong everywhere).
When you access A.two
, it's returning the raw function object (the staticmethod
descriptor doesn't need to do anything special aside from preventing binding that would pass self
or cls
, so it just returns what it wrapped). But that raw function object then gets attached to B
as an unbound instance method, because without the staticmethod
wrapping, it's just like you'd defined it normally.
The reason the latter works in Python 3 is that Python 3 has no concept of unbound methods. It has functions (which if accessed via an instance of a class become bound methods) and bound methods, where Python 2 has functions, unbound methods and bound methods.
Unbound methods check that they're called with an object of the correct type, thus your error. Plain functions just want the correct number of arguments.
The staticmethod
decorator in Python 3 is still returning the raw function object, but in Python 3, that's fine; since it's not a special unbound method object, if you call it on the class itself, it's just like a namespaced function, not a method of any sort. You'd see the problem if you tried to do:
B().two()
though, because that will make a bound method out of that instance of B
and the two
function, passing an extra argument (self
) that two
does not accept. Basically, on Python 3, staticmethod
is a convenience to let you call the function on instances without causing binding, but if you only ever call the function by referencing the class itself, it's not needed, because it's just a plain function, not the Python 2 "unbound method".
If you had some reason to perform this copy (normally, I'd suggest inheriting from A
, but whatever), and you want to make sure you get the descriptor wrapped version of the function, not whatever the descriptor gives you when accessed on A
, you'd bypass the descriptor protocol by directly accessing A
's __dict__
:
class B(object): one = A.__dict__['one'] two = A.__dict__['two']
By directly copying from the class dictionary, the descriptor protocol magic is never invoked, and you get the staticmethod
and classmethod
wrapped versions of one
and two
.
DISCLAIMER: This is not really an answer, but it doesn't fit into a comment format either.
Note that with Python2 @classmethod
is NOT correctly preserved across classes either. In the code below, the call to B.one()
works as though it was invoked through class A
:
$ cat test.py class A(object): @classmethod def one(cls): print("I am class", cls.__name__) class A2(A): pass class B(object): one = A.one A.one() A2.one() B.one() $ python2 test.py ('I am class', 'A') ('I am class', 'A2') ('I am class', 'A')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With