Check if a function uses @classmethod

十年热恋 提交于 2020-01-12 03:26:34

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!