问题
TL;DR How do I find out whether a function was defined using @classmethod
or something with the same effect?
My problem
For implementing a class decorator I would like to check if a method takes the class as its first argument, for example as achieved via
@classmethod
def function(cls, ...):
I found a solution to check for @staticmethod
via the types
module (isinstance(foo, types.UnboundMethodType)
is False
if the foo
is static, see here), but did not find anything on how to do so for @classmethod
Context
What I am trying to do is something along the lines of
def class_decorator(cls):
for member in cls.__dict__:
if (isclassmethod(getattr(cls, member))):
# do something with the method
setattr(cls, member, modified_method)
return cls
and I do not know how to implement what I called isclassmethod
in this example
回答1:
For Python 2, you need to test both if the object is a method, and if __self__
points to the class (for regular methods it'll be None
when retrieved from the class):
>>> class Foo(object):
... @classmethod
... def bar(cls):
... pass
... def baz(self):
... pass
...
>>> Foo.baz
<unbound method Foo.baz>
>>> Foo.baz.__self__
>>> Foo.baz.__self__ is None
True
>>> Foo.bar.__self__
<class '__main__.Foo'>
>>> Foo.bar.__self__ is Foo
True
In Python 3, regular methods show up as functions (unbound methods have been done away with).
Combine this with inspect.ismethod() for a fail-safe method to detect a class method in both Python 2 and 3:
import inspect
if inspect.ismethod(cls.method) and cls.method.__self__ is cls:
# class method
The method.__self__
attribute was added in Python 2.6 to be consistent with Python 3. In Python 2.6 and 2.7 it is an alias of method.im_self
.
回答2:
You should use inspect.ismethod. It works because classmethod binds the function to the class object. See the following code:
>>> class Foo:
... @classmethod
... def bar():
... pass
... def baz():
... pass
...
>>> Foo.bar
<bound method type.bar of <class '__main__.Foo'>>
>>> Foo.baz
<function Foo.baz at 0x0000000002CCC1E0>
>>> type(Foo.bar)
<class 'method'>
>>> type(Foo.baz)
<class 'function'>
>>> import inspect
>>> inspect.ismethod(Foo.bar)
True
>>> inspect.ismethod(Foo.baz)
False
回答3:
class Foo(object):
@classmethod
def baaz(cls):
print "baaz"
isinstance(Foo.__dict__["baaz"], classmethod)
回答4:
None of the answers address the problem of identifying whether a method is decorated with class method from an instance of the class. Following code explores the class dict of an instance to distinguish between classmethod from other methods.
class MyClass(object):
@classmethod
def class_method(cls):
pass
def instance_method(self):
pass
@staticmethod
def static_method():
pass
def blas(): pass
t = MyClass()
isinstance(t.__class__.__dict__[t.class_method.__name__], classmethod) # True
isinstance(t.__class__.__dict__[t.static_method.__name__], classmethod) # False
isinstance(t.__class__.__dict__[t.instance_method.__name__], classmethod) # False
isinstance(t.__class__.__dict__[t.blas.__name__], classmethod) # False
This will work for both Python 2 and 3.
回答5:
This works for me:
def is_classmethod(method):
"""
Is method a classmethod?
"""
return isinstance(getattr(method, '__self__', None), type)
It basically tests if method.__self__
exists and is a class, as in Martijn's answer, but does not require access to the class itself.
来源:https://stackoverflow.com/questions/19227724/check-if-a-function-uses-classmethod