问题
While parsing attributes using __dict__, my @staticmethod is not 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'>
- How is this possible?
- How to check if a static method is callable?
I provide below a more detailed example:
Script 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()
Result for python2
> python2.7 test.py
Is bar() callable? True
Is foo() callable? True
bar True <type 'function'>
foo False <type 'staticmethod'>
Same result for python3
> python3.4 test.py
Is bar() callable? True
Is foo() callable? True
bar True <class 'function'>
foo False <class 'staticmethod'>
回答1:
The reason for this behavior is the descriptor protocol. The C.foo won't return a staticmethod but a normal function while the 'foo' in __dict__ is a staticmethod (and staticmethod is a descriptor).
In short C.foo isn't the same as C.__dict__['foo'] in this case - but rather C.__dict__['foo'].__get__(C) (see also the section in the documentation of the Data model on descriptors):
>>> 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
In your case I would check for callables using getattr (which knows about descriptors and how to access them) instead of what is stored as value in the class __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))
Which prints (on python-3.x):
Is bar() callable? True
Is foo() callable? True
bar True <class 'function'>
foo True <class 'function'>
The types are different on python-2.x but the result of callable is the same:
Is bar() callable? True
Is foo() callable? True
bar True <type 'instancemethod'>
foo True <type 'function'>
回答2:
You can't check if a staticmethod object is callable or not. This was discussed on the tracker in Issue 20309 -- Not all method descriptors are callable and closed as "not a bug".
In short, there's been no rationale for implementing __call__ for staticmethod objects. The built-in callable has no way to know that the staticmethod object is something that essentially "holds" a callable.
Though you could implement it (for staticmethods and classmethods), it would be a maintenance burden that, as previously mentioned, has no real motivating use-cases.
For your case, you can use getattr(C, name) to perform a look-up for the object named name; this is equivalent to performing C.<name>. getattr, after finding the staticmethod object, will invoke its __get__ to get back the callable it's managing. You can then use callable on that.
A nice primer on descriptors can be found in the docs, take a look at Descriptor HOWTO.
来源:https://stackoverflow.com/questions/45375944/python-static-method-is-not-always-callable