Проверьте, использует ли функция @classmethod
TL; DR. Как узнать, была ли определена функция с помощью @classmethod
или с тем же эффектом?
Моя проблема
Для реализации декоратора класса я хотел бы проверить, принимает ли метод этот класс в качестве первого аргумента, например, достигнутый с помощью
@classmethod
def function(cls, ...):
Я нашел решение для проверки @staticmethod
через модуль types
(isinstance(foo, types.UnboundMethodType)
is False
, если foo
является статическим, см. здесь), но не нашел ничего о том, как это сделать для @classmethod
Контекст
То, что я пытаюсь сделать, это что-то вроде строк
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
и я не знаю, как реализовать то, что я назвал isclassmethod
в этом примере
Ответы
Ответ 1
Для Python 2 вам нужно проверить оба, если объект является методом, и если __self__
указывает на класс (для обычных методов он будет None
при извлечении из класса):
>>> 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
В Python 3 регулярные методы отображаются как функции (с помощью несвязанных методов).
Объедините это с inspect.ismethod()
для отказобезопасного метода для обнаружения метода класса как в Python 2, так и в 3:
import inspect
if inspect.ismethod(cls.method) and cls.method.__self__ is cls:
# class method
Атрибут method.__self__
был добавлен в Python 2.6 для соответствия Python 3. В Python 2.6 и 2.7 он является псевдонимом method.im_self
.
Ответ 2
Вы должны использовать inspect.ismethod. Он работает, потому что classmethod связывает функцию с объектом класса. См. Следующий код:
>>> 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
Это работает для меня:
def is_classmethod(method):
"""
Is method a classmethod?
"""
return isinstance(getattr(method, '__self__', None), type)
В основном он проверяет, существует ли method.__self__
и является классом, как в ответе Martijn, но не требует доступа к самому классу.