检查函数是否使用@classmethod [英] Check if a function uses @classmethod
问题描述
TL; DR 如何确定某个函数是使用 @classmethod
定义的还是具有相同效果的东西?
TL;DR How do I find out whether a function was defined using @classmethod
or something with the same effect?
我的问题
用于实施课程装饰器我想检查一个方法是否将类作为其第一个参数,例如通过
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, ...):
我找到了一种检查<$的解决方案c $ c> @staticmethod 通过 types
模块( isinstance(foo,types.UnboundMethodType)$ c $如果
foo
是静态的,则c>为 False
,请参见此处),但没有找到有关 @classmethod
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
上下文
我正在尝试做的事情与
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
在此示例中
and I do not know how to implement what I called isclassmethod
in this example
推荐答案
如果对象是方法对象,依此类推具有 方法。__self __
属性,并且该属性是您从中获得该属性的类,那么它将把该类作为第一个参数。它已经绑定到该类。
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.
请注意,此时您已经具有一个绑定对象,因此您无需传递类< ,除非您首先从方法中提取原始函数。__func __
。
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__
.
这里是一个说明, class Foo
有一个类方法 bar
和一个常规方法 baz
,当您直接在类上访问它时不受限制:
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
调用 Foo.bar()
自动传递 Foo.bar .__ self __
作为第一个参数。
Calling Foo.bar()
automatically passes in Foo.bar.__self__
as the first argument.
如果需要测试这种方法,请使用 inspect.ismethod()
,如果返回 True
,请测试 __ self __
属性:
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
这应该适用于任何 custom 描述符,它们的作用类似于
This should work for any custom descriptors that work like classmethod
does, as well.
如果您需要确定地知道该方法是由 classmethod $ c $产生的,则也可以使用classmethod
。 c>对象,则需要直接在类名称空间中查找属性( cls .__ dict __
或 vars(cls)
),并在方法解析的类层次结构中的每个类中进行操作顺序:
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
,并使用基类和派生类对上述两种方法进行全面测试,并使用自定义描述符绑定函数的方式与 classmethod
相同,但本身不是 classmethod
:
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
isclassmethod()
函数可以正确区分 classmethod
和 notclassmethod
描述符。
The isclassmethod()
function correctly distinguishes between the classmethod
and notclassmethod
descriptors.
历史记录:该答案包括对Python 2的引用,但在Python 2达到EOL时被删除,因为不再相关。
Historical note: this answer included references to Python 2, but with Python 2 having reached EOL were removed as no longer relevant.
这篇关于检查函数是否使用@classmethod的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!