如何创建一个能够包装实例,类和静态方法的Python类装饰器? [英] How to create a Python class decorator that is able to wrap instance, class and static methods?
问题描述
我想创建一个Python 类装饰器(*),它将能够无缝包装该类可能具有的所有方法类型:实例,类和静态。
I'd like to create a Python class decorator (*) that would be able to seamlessly wrap all method types the class might have: instance, class and static.
这是我现在拥有的代码,带有注释的部分已注释:
This is the code I have for now, with the parts that break it commented:
def wrapItUp(method):
def wrapped(*args, **kwargs):
print "This method call was wrapped!"
return method(*args, **kwargs)
return wrapped
dundersICareAbout = ["__init__", "__str__", "__repr__"]#, "__new__"]
def doICareAboutThisOne(cls, methodName):
return (callable(getattr(cls, methodName))
and (not (methodName.startswith("__") and methodName.endswith("__"))
or methodName in dundersICareAbout))
def classDeco(cls):
myCallables = ((aname, getattr(cls, aname)) for aname in dir(cls) if doICareAboutThisOne(cls, aname))
for name, call in myCallables:
print "*** Decorating: %s.%s(...)" % (cls.__name__, name)
setattr(cls, name, wrapItUp(call))
return cls
@classDeco
class SomeClass(object):
def instanceMethod(self, p):
print "instanceMethod: p =", p
@classmethod
def classMethod(cls, p):
print "classMethod: p =", p
@staticmethod
def staticMethod(p):
print "staticMethod: p =", p
instance = SomeClass()
instance.instanceMethod(1)
#SomeClass.classMethod(2)
#instance.classMethod(2)
#SomeClass.staticMethod(3)
#instance.staticMethod(3)
我有两个问题要尝试完成此工作:
I'm having two issues trying to make this work:
- 遍历所有可调用对象时,如何确定它是实例,类还是静态类型?
- 如何用适当的包装版本覆盖该方法,
当前,此代码会生成不同的 TypeError
s取决于未注释的摘录片段,例如:
Currently, this code generates different TypeError
s depending on what commented snippet is uncommented, like:
-
TypeError:必须绑定未绑定的方法wrapd()以SomeClass实例作为第一个参数调用(改为int实例)
-
TypeError:classMethod()恰好接受2个参数(给定3个)
TypeError: unbound method wrapped() must be called with SomeClass instance as first argument (got int instance instead)
TypeError: classMethod() takes exactly 2 arguments (3 given)
(*):如果您直接装饰方法。
推荐答案
由于方法是函数的包装器,因此在构造类之后,要在类的方法上应用装饰器,您必须:
Because methods are wrappers for functions, to apply a decorator to a method on a class after the class has been constructed, you have to:
- 使用其
im_func
属性从方法中提取基础函数。 - 装饰函数。
- 重新应用包装器。
- 用包装后的装饰函数覆盖属性。
- Extract the underlying function from the method using its
im_func
attribute. - Decorate the function.
- Re-apply the wrapper.
- Overwrite the attribute with the wrapped, decorated function.
一旦类方法
与常规方法区分开> @classmethod 装饰器已被应用;两种方法的类型均为 instancemethod
。但是,您可以检查 im_self
属性,查看它是否为 None
。如果是这样,这是一个常规实例方法;否则,这是类方法
。
It is difficult to distinguish a classmethod
from a regular method once the @classmethod
decorator has been applied; both kinds of methods are of type instancemethod
. However, you can check the im_self
attribute and see whether it is None
. If so, it's a regular instance method; otherwise it's a classmethod
.
静态方法是简单的函数( @staticmethod
装饰器仅阻止应用常规方法包装器)。因此,您不必为此做任何特殊的事情。
Static methods are simple functions (the @staticmethod
decorator merely prevents the usual method wrapper from being applied). So you don't have to do anything special for these, it looks like.
所以基本上您的算法如下:
So basically your algorithm looks like this:
- 获取属性。
- 可调用吗?如果不是,请转到下一个属性。
- 其类型是否为
types.MethodType
?如果是这样,则它是类方法或实例方法。
- 如果其
im_self
为None
,则为实例方法。通过im_func
属性提取基础函数,进行修饰,然后重新应用实例方法:meth = types.MethodType(func,None,cls )
- 如果其
im_self
不是None
,这是一个类方法。通过im_func
提取基础函数并进行装饰。现在,您必须重新应用classmethod
装饰器,但是您不能这样做,因为classmethod()
不上课,因此无法指定它将附加到的类。相反,您必须使用实例方法装饰器:meth = types.MethodType(func,cls,type)
。请注意,这里的type
是实际的内置type
。
- 如果其
- Get the attribute.
- Is it callable? If not, proceed to the next attribute.
- Is its type
types.MethodType
? If so, it is either a class method or an instance method.- If its
im_self
isNone
, it is an instance method. Extract the underlying function via theim_func
attribute, decorate that, and re-apply the instance method:meth = types.MethodType(func, None, cls)
- If its
im_self
is notNone
, it is a class method. Exctract the underlying function viaim_func
and decorate that. Now you have to reapply theclassmethod
decorator but you can't becauseclassmethod()
doesn't take a class, so there's no way to specify what class it will be attached to. Instead you have to use the instance method decorator:meth = types.MethodType(func, cls, type)
. Note that thetype
here is the actual built-in,type
.
- If its
这些变化在Python中有所改变3-IIRC是函数的绑定方法。无论如何,这可能需要在这里完全重新考虑。
These change somewhat in Python 3 -- unbound methods are functions there, IIRC. In any case this will probably need to be completely rethought there.
这篇关于如何创建一个能够包装实例,类和静态方法的Python类装饰器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!