Check if a function uses @classmethod
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.
class Foo(object):
@classmethod
def baaz(cls):
print "baaz"
isinstance(Foo.__dict__["baaz"], classmethod)
If the object is a method object, and so has a method.__self__
attribute, and that attribute is the class you got the attribute from, then it'll take the class as the first argument. It has been bound to the class.
Note that you already have a bound object at this point, so you don't need to pass in the class again, unless you first extract the original function from method.__func__
.
Here is an illustration, the class Foo
has a class method bar
and a regular method baz
, which is not bound when you access it directly on the class:
>>> class Foo:
... @classmethod
... def bar(cls):
... pass
... def baz(self):
... pass
...
>>> Foo.baz
<function Foo.baz at 0x1097d1e18>
>>> Foo.bar
<bound method Foo.bar of <class '__main__.Foo'>>
>>> Foo.bar.__self__
<class '__main__.Foo'>
>>> Foo.bar.__self__ is Foo
True
Calling Foo.bar()
automatically passes in Foo.bar.__self__
as the first argument.
If you need to test such methods, use inspect.ismethod()
, and if that returns True
test the __self__
attribute:
import inspect
if inspect.ismethod(cls.method) and cls.method.__self__ is cls:
# method bound to the class, e.g. a classmethod
This should work for any custom descriptors that work like classmethod
does, as well.
If you need to know with certainty that the method was produced by a classmethod
object, you'll need to look up the attributes directly in the class namespace (cls.__dict__
or vars(cls)
), and do so in each class in the class hierarchy in method resolution order:
def isclassmethod(method):
bound_to = getattr(method, '__self__', None)
if not isinstance(bound_to, type):
# must be bound to a class
return False
name = method.__name__
for cls in bound_to.__mro__:
descriptor = vars(cls).get(name)
if descriptor is not None:
return isinstance(descriptor, classmethod)
return False
and a full test of the above two approaches using a base class and a derived class, with a custom descriptor that binds a function the same way a classmethod
would, but is not, itself, a classmethod
:
>>> class notclassmethod:
... def __init__(self, f):
... self.f = f
... def __get__(self, _, typ=None):
... return self.f.__get__(typ, typ)
...
>>> class Base:
... @classmethod
... def base_cm(cls): pass
... @notclassmethod
... def base_ncm(cls): pass
... def base_m(self): pass
...
>>> class Derived(Base):
... @classmethod
... def derived_cm(cls): pass
... @notclassmethod
... def derived_ncm(cls): pass
... def derived_m(self): pass
...
>>> inspect.ismethod(Derived.base_cm) and Derived.base_cm.__self__ is Derived
True
>>> inspect.ismethod(Derived.base_ncm) and Derived.base_ncm.__self__ is Derived
True
>>> inspect.ismethod(Derived.base_m) and Derived.base_m.__self__ is Derived
False
>>> inspect.ismethod(Derived.derived_cm) and Derived.derived_cm.__self__ is Derived
True
>>> inspect.ismethod(Derived.derived_ncm) and Derived.derived_ncm.__self__ is Derived
True
>>> inspect.ismethod(Derived.derived_m) and Derived.derived_m.__self__ is Derived
False
>>> isclassmethod(Derived.base_cm)
True
>>> isclassmethod(Derived.base_ncm)
False
>>> isclassmethod(Derived.base_m)
False
>>> isclassmethod(Derived.derived_cm)
True
>>> isclassmethod(Derived.derived_ncm)
False
>>> isclassmethod(Derived.derived_m)
False
The isclassmethod()
function correctly distinguishes between the classmethod
and notclassmethod
descriptors.
Historical note: this answer included references to Python 2, but with Python 2 having reached EOL were removed as no longer relevant.
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