First of all, I understand how, in general, a decorator work. And I know @staticmethod
strips off the instance argument in the signature, making
class C(object):
@staticmethod
def foo():
print 'foo'
C.foo //<function foo at 0x10efd4050>
C().foo //<function foo at 0x10efd4050>
valid.
However, I don't understand how the sourcec code of staticmethod
make this happen.
It seems to me that when wrapping method foo
in staticmethod
, an instance of staticmethod
is instantiated, then some magic happens, making C.foo()
legit.
So.. what happen in those magic? what did staticmethod
do?
I'm aware the enormous topics on SO regarding staticmethods
but none of them addresses my doubts. But maybe I didn't hit the magic keyword. If so, please kindly let me know.
For whoever looking for staticmethod
source code, please refer to https://hg.python.org/cpython/file/c6880edaf6f3/Objects/funcobject.c
A staticmethod
object is a descriptor. The magic you are missing is that Python calls the __get__
method when accessing the object as an attribute on a class or instance.
So accessing the object as C.foo
results in Python translating that to C.__dict__['foo'].__get__(None, C)
, while instance_of_C.foo
becomes type(instace_of_C).__dict__['foo'].__get__(instance_of_C, type(instance_of_C))
.
The staticmethod
object is defined in C code, but an equivalent in Python would be:
class staticmethod(object):
def __init__(self, callable):
self.f = callable
def __get__(self, obj, type=None):
return self.f
@property
def __func__(self):
return self.f
where self.f
is the original wrapped function.
All this is needed because functions are themselves descriptors too; it is the descriptor protocol that gives you method objects (see python bound and unbound method object for more details). Since they too have a __get__
method, without a staticmethod
object wrapping the function, a functionobj.__get__
call produces a method object instead, passing in a self
argument.
There is also a classmethod
, which uses the second argument to descriptor.__get__
to bind a function to the class, and then there are property
objects, which translate binding into a function call directly. See How does the @property decorator work?.