Python 检查函数是否使用@classmethod
TL;DR如何确定函数是使用Python 检查函数是否使用@classmethod,python,decorator,class-method,python-decorators,Python,Decorator,Class Method,Python Decorators,TL;DR如何确定函数是使用@classmethod定义的还是具有相同效果的 我的问题 为了实现类装饰器,我想检查一个方法是否将类作为其第一个参数,例如通过 @classmethod def function(cls, ...): 我找到了一个通过类型模块检查@staticmethod的解决方案(isinstance(foo,types.UnboundMethodType)为False如果foo是静态的,请参见),但没有找到任何关于如何为@classmethod执行此操作的信息 上下文 我
@classmethod
定义的还是具有相同效果的
我的问题 为了实现类装饰器,我想检查一个方法是否将类作为其第一个参数,例如通过
@classmethod
def function(cls, ...):
我找到了一个通过类型模块检查@staticmethod
的解决方案(isinstance(foo,types.UnboundMethodType)
为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
,您应该使用它。它之所以有效,是因为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
>>类Foo:
... @类方法
... def bar():
... 通过
... def baz():
... 通过
...
>>>美食酒吧
>>>福巴兹
>>>类型(食物棒)
>>>类型(Foo.baz)
>>>进口检验
>>>检查方法(食物棒)
真的
>>>检验方法(食品添加剂)
假的
如果对象是一个方法对象,因此有一个属性,并且该属性是从中获取属性的类,那么它将把该类作为第一个参数。它已绑定到该类
请注意,此时您已经有一个绑定对象,因此不需要再次传入该类,除非您首先从方法中提取原始函数
下面是一个示例,类Foo
有一个类方法bar
和一个常规方法baz
,当您直接在类上访问它时,它不会被绑定:
>>> 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
这应该适用于像classmethod
那样工作的任何自定义描述符
如果您需要确定该方法是由classmethod
对象生成的,则需要直接在类名称空间(cls.\uu dict\uuu
或vars(cls)
)中查找属性,并在类层次结构中的每个类中进行查找:
使用一个基类和一个派生类对上述两种方法进行全面测试,使用一个自定义描述符绑定函数,与classmethod
绑定函数的方式相同,但其本身不是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
描述符
历史注释:此答案包括对Python 2的引用,但由于Python 2已达到EOL,因此被删除为不再相关。这对我来说很有用:
class Foo(object):
@classmethod
def baaz(cls):
print "baaz"
isinstance(Foo.__dict__["baaz"], classmethod)
def is_classmethod(method):
"""
Is method a classmethod?
"""
return isinstance(getattr(method, '__self__', None), type)
它基本上测试方法。\uuuuu self\uuuu
是否存在并且是一个类,如Martijn的答案所示,但不需要访问类本身。没有一个答案解决了从类实例识别方法是否用类方法修饰的问题。下面的代码探索实例的类dict,以区分classmethod和其他方法
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
这将适用于Python 2和Python 3。这仅适用于Python 3是否有任何东西同时适用于Python 2和Python 3(我个人使用的是2.7)@hlt您可以在下面检查我的答案。这似乎是一种泛型。我们不能保证类型在所有情况下都是classmethod。更好的方法是使用_uname_uu属性而不是字符串作为name。在python中,我们尽可能避免松散的字符串。isinstance(Foo.uu dict_uu[Foo.baaz.uuu name_uuu],classmethod)@SaimRaza虽然我完全同意您的评论,但您必须注意,该方法的\uu name\uuuuu
可能不一定与属性名匹配。由于我们可以从dir(cls)
(或者,在OP示例中,直接从cls.\uuu dict\uuuu
)获取属性名,因此使用属性名比使用方法的规范名更安全。您能用一个示例详细说明一下吗?我假设我们的范围仅限于方法。你指的是Python3中的u_name_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu和_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,ieFoo.bar=baaz
(其中Foo
是一个类,而baaz
是一个函数)。不,我说的不是\uuuuuu qualname\uuuuu
,而是属性名(bar
,在上面的例子中)与函数的\uuuu name\uuuuuuuz
属性值(baaz
)的比较。inspect.ismethod(cls.method)
在Python 3.5+中为False。@fx kirin:no,对于classmethod
对象,它是True
。您将它与常规函数一起使用,然后您将在任何Python 3版本中看到False
,而不仅仅是3.5+。这在我的答案中有说明。@fx kirin:我已经更新了答案,以便更清楚地了解这里的目标,即检测使用@classmethod
修饰的函数的结果,然后查找该类。
def is_classmethod(method):
"""
Is method a classmethod?
"""
return isinstance(getattr(method, '__self__', None), type)
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