What magic does staticmethod () do so that a static method is always called without an instance parameter?

I am trying to understand how static methods work internally. I know how to use the @staticmethod decorator, but I will avoid using it in this post to dive deeper into how static methods work and ask my questions.

From what I know about Python, if there is a class A , then calling A.foo() calls foo() without arguments, while calling A().foo() calls foo() with one argument, where this one argument is an instance of A() .

However, in the case of static methods, it seems that always foo() is called without arguments, whether we call it A.foo() or A().foo() .

Proof below:

 >>> class A: ... x = 'hi' ... def foo(): ... print('hello, world') ... bar = staticmethod(foo) ... >>> A.bar() hello, world >>> A().bar() hello, world >>> A.bar <function A.foo at 0x00000000005927B8> >>> A().bar <function A.foo at 0x00000000005927B8> >>> A.bar(1) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: foo() takes 0 positional arguments but 1 was given >>> A().bar(1) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: foo() takes 0 positional arguments but 1 was given 

So, I'm right in concluding that the staticmethod() function does some magic, so that foo() always called with 0 arguments?

If I defined my own staticmethod() in my own Python code, how would I do it? Is it even possible to define such a method from our own Python code, or can such a function be defined only as inline?

+6
source share
2 answers

It is implemented as a descriptor . For instance:

 In [1]: class MyStaticMethod(object): ...: def __init__(self, func): ...: self._func = func ...: def __get__(self, inst, cls): ...: return self._func ...: In [2]: class A(object): ...: @MyStaticMethod ...: def foo(): ...: print('Hello, World!') ...: In [3]: A.foo() Hello, World! In [4]: A().foo() Hello, World! 

In the same way, you can define a classmethod by simply passing cls to the original function:

 In [5]: from functools import partial ...: ...: class MyClassMethod(object): ...: def __init__(self, func): ...: self._func = func ...: def __get__(self, inst, cls): ...: return partial(self._func, cls) In [6]: class A(object): ...: @MyClassMethod ...: def foo(cls): ...: print('In class: {}'.format(cls)) ...: In [7]: A.foo() In class: <class '__main__.A'> In [8]: A().foo() In class: <class '__main__.A'> 
+8
source

Methods, including instances, static and class methods, work through the descriptor protocol . If an object in the dict class implements the special __get__ method:

 class Descriptor(object): def __get__(self, instance, klass): return instance, klass class HasDescriptor(object): descriptor = Descriptor() x = HasDescriptor() 

then access to the following attribute:

 x.descriptor HasDescriptor.descriptor 

will call the __get__ descriptor __get__ to calculate their value, for example:

 descriptor.__get__(x, HasDescriptor) descriptor.__get__(None, HasDescriptor) 

Functions, staticmethod and classmethod all implement __get__ to make the method accessible. You can do the same:

 class MyStaticMethod(object): def __init__(self, f): self.f = f def __get__(self, instance, klass): return self.f 

There are also __set__ and __delete__ methods that allow you to control the setting and removal of attributes, respectively. Methods do not use them, but property does.

+5
source

All Articles