staticmethod()做了什么魔术,所以静态方法总是在没有实例参数的情况下被调用?

时间:2014-03-10 07:34:40

标签: python python-3.x static-methods

我试图了解静态方法如何在内部工作。我知道如何使用@staticmethod装饰器,但我将避免在这篇文章中使用它,以深入了解静态方法的工作原理并提出我的问题。

根据我对Python的了解,如果有一个类A,则调用A.foo()调用foo()时不带参数,而调用A().foo()调用foo()使用一个参数,其中一个参数是实例A()本身。

但是,在静态方法的情况下,无论我们将其称为foo()还是A.foo(),似乎总是调用A().foo()而不使用参数。

以下证明:

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

所以我正确地得出结论staticmethod()函数有一些魔力使得foo()总是用0参数调用?

如果我在自己的Python代码中定义自己的staticmethod(),我该怎么做?甚至可以从我们自己的Python代码中定义这样的方法,还是只能将这样的函数定义为内置函数?

2 个答案:

答案 0 :(得分:9)

它是以descriptor实现的。例如:

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!

以同样的方式定义classmethod,只需将cls传递给原始函数:

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'>

答案 1 :(得分:5)

方法(包括实例,静态和类方法)通过descriptor protocol完成。如果类dict中的对象实现了__get__特殊方法:

class Descriptor(object):
    def __get__(self, instance, klass):
        return instance, klass

class HasDescriptor(object):
    descriptor = Descriptor()

x = HasDescriptor()

然后访问以下属性:

x.descriptor
HasDescriptor.descriptor

将调用描述符的__get__方法来计算它们的值,如下所示:

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

函数,staticmethodclassmethod都实现__get__以使方法访问起作用。你也可以这样做:

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

还有__set____delete__方法,分别允许您控制设置和删除属性。方法不使用这些,但property会这样做。