Python静态方法并不总是可调用的

oli*_*bre 9 python static-methods callable python-2.7 python-3.x

在使用解析属性时__dict__,我@staticmethod不是callable.

Python 2.7.5 (default, Aug 29 2016, 10:12:21)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from __future__ import (absolute_import, division, print_function)
>>> class C(object):
...   @staticmethod
...   def foo():
...     for name, val in C.__dict__.items():
...       if name[:2] != '__':
...          print(name, callable(val), type(val))
...
>>> C.foo()
foo  False  <type 'staticmethod'>
Run Code Online (Sandbox Code Playgroud)
  • 这怎么可能?
  • 如何检查静态方法是否可调用?

我在下面提供了一个更详细的例子:

脚本 test.py

from __future__ import (absolute_import, division, print_function)

class C(object):

  @staticmethod
  def foo():
    return 42

  def bar(self):
    print('Is bar() callable?', callable(C.bar))
    print('Is foo() callable?', callable(C.foo))
    for attribute, value in C.__dict__.items():
      if attribute[:2] != '__':
        print(attribute, '\t', callable(value), '\t', type(value))

c = C()
c.bar()
Run Code Online (Sandbox Code Playgroud)

python2的结果

> python2.7 test.py
Is bar() callable? True
Is foo() callable? True
bar      True    <type 'function'>
foo      False   <type 'staticmethod'>
Run Code Online (Sandbox Code Playgroud)

python3的结果相同

> python3.4 test.py
Is bar() callable? True
Is foo() callable? True
bar      True    <class 'function'>
foo      False   <class 'staticmethod'>
Run Code Online (Sandbox Code Playgroud)

MSe*_*ert 10

这种行为的原因是描述符协议.当in 是a (并且是描述符)时,C.foo将不返回staticmethod但是正常函数.'foo'__dict__staticmethodstaticmethod

简而言之C.foo,与C.__dict__['foo']这种情况不同 - 而是C.__dict__['foo'].__get__(C)(另请参阅描述符数据模型文档中的部分):

>>> callable(C.__dict__['foo'].__get__(C))
True
>>> type(C.__dict__['foo'].__get__(C))
function

>>> callable(C.foo)
True
>>> type(C.foo)
function

>>> C.foo is C.__dict__['foo'].__get__(C)
True
Run Code Online (Sandbox Code Playgroud)

在你的情况下,我会检查callables使用getattr(知道描述符和如何访问它们)而不是在类中存储的值__dict__:

def bar(self):
    print('Is bar() callable?', callable(C.bar))
    print('Is foo() callable?', callable(C.foo))
    for attribute in C.__dict__.keys():
        if attribute[:2] != '__':
            value = getattr(C, attribute)
            print(attribute, '\t', callable(value), '\t', type(value))
Run Code Online (Sandbox Code Playgroud)

哪个打印(在python-3.x上):

Is bar() callable? True
Is foo() callable? True
bar      True    <class 'function'>
foo      True    <class 'function'>
Run Code Online (Sandbox Code Playgroud)

python-2.x上的类型不同,但结果callable是相同的:

Is bar() callable? True
Is foo() callable? True
bar      True    <type 'instancemethod'>
foo      True    <type 'function'>
Run Code Online (Sandbox Code Playgroud)