Can someone explain how the source code of staticmethod works in python
Asked Answered
A

2

27

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

Adagio answered 10/8, 2015 at 9:28 Comment(0)
M
28

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?.

Mallina answered 10/8, 2015 at 9:31 Comment(5)
I got the source code from PyCharm IDE.. which I have no idea whether it is the real copy..oopsAdagio
@user2829759: PyCharm can't access the actual source code (as it is compiled C code) so it reconstructs a shim for you from introspecting the object.Mallina
@martijinpieters Does it reflect what's happening in the C code?Adagio
@user2829759: My Python code version? Absolutely. The PyCharm reflection cannot show what happens in the methods, it can only infer a signature.Mallina
@user2829759: the __func__ property description is utterly wrong, however. It simply returns the wrapped function (just like the __get__ method does, basically). The __getattribute__ and __new__ methods being there is just confusing matters.Mallina
F
0

The source code of static method is actually written in C, but you can replicate its behavior with this short snippet:

class staticmethod:
  
    def __init__(self, decorated_method):
        self.decorated_method = decorated_method
        
    def __get__(self, instance, owner):
        return self.decorated_method

Here is brief explanation of the above snippet, from this article I wrote: “Methods are descriptors and hence implement the magic method __get__. @staticmethod is a class decorator, reimplementing __get__ and making it return a version of the decorated method that has not been bound to the object it belongs to. Hence, the decorated method does not have the implicit argument self.” If the notions of descriptors, bound methods or class descriptors are not clear to you, they are explained in the article.

Frobisher answered 6/3, 2022 at 11:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.