如何装饰对象方法?
我需要给一个对象的方法加装饰。这个装饰需要在运行时进行,因为应用在对象上的装饰取决于用户在调用程序时提供的参数(通过argv传递的参数),所以同一个对象可能会被装饰3次、2次,或者根本不被装饰。
这里有点背景,这个程序是一个解谜工具,主要功能是自动找到谜题的解决方案。这里的“自动”是指不需要用户干预。而装饰的作用就在这里,我想做的一件事是绘制执行过程中发生的事情的图表,但我只想在使用了--draw-graph
这个标志时才进行绘制。
这是我尝试过的:
class GraphDecorator(object):
def __init__(self, wrappee):
self.wrappee = wrappee
def method(self):
# do my stuff here
self.wrappee.method()
# do more of stuff here
def __getattr__(self,attr):
return getattr(self.wrappee,attr)
为什么它没有成功:
没有成功的原因是我构建应用程序的方式。当调用一个在我的装饰类中不存在的方法时,它会回退到被装饰类的实现。问题在于,应用程序总是首先调用run
这个不需要装饰的方法,因此使用了未装饰的回退形式,而在未装饰的形式中,它总是调用未装饰的方法。我需要的是替换对象的方法,而不是代理它:
# method responsible to replace the undecorated form by the decorated one
def graphDecorator(obj):
old_method = obj.method
def method(self):
# do my stuff here
old_method()
# do more of my stuff
setattr(obj,'method',method) # replace with the decorated form
我的问题是,装饰后的形式在被调用时没有接收到self
,这导致了因为参数数量不对而出现的TypeError错误。
4 个回答
你可能想用 __getattribute__
来代替 __getattr__
,因为后者只有在“标准”查找失败时才会被调用:
class GraphDecorator(object):
def __init__(self, wrappee):
self.__wrappee = wrappee
def method(self):
# do my stuff here
self.wrappe.method()
# do more of stuff here
def __getattribute__(self, name):
try:
wrappee = object.__getattribute__(self, "_GraphDecorator__wrappee")
return getattr(wrappee, name)
except AttributeError:
return object.__getattribute__(self, name)
我需要给一个对象的方法加装饰。这个装饰需要在运行时进行,因为应用在对象上的装饰取决于用户在调用程序时提供的参数(通过argv传入的参数),所以同一个对象可能会被装饰3次、2次,或者根本不被装饰。
上面的说法不太对,而且你想做的事情其实没必要。你可以像这样在运行时进行操作。示例:
import sys
args = sys.argv[1:]
class MyClass(object):
pass
if args[0]=='--decorateWithFoo':
MyClass = decoratorFoo(MyClass)
if args[1]=='--decorateWithBar'
MyClass = decoratorBar(MyClass)
语法:
@deco
define something
和下面这个是一样的:
define something
something = deco(something)
你也可以创建一个装饰器工厂 @makeDecorator(command_line_arguments)
问题在于我不能把func(self)当作一个方法来用。原因是setattr()这个方法并没有把函数绑定到类上,所以这个函数就像一个静态方法,而不是一个类方法。由于Python具有自省的特性,我想出了这个解决方案:
def decorator(obj):
old_func = obj.func # can't call 'by name' because of recursion
def decorated_func(self):
# do my stuff here
old_func() # does not need pass obj
# do some othere stuff here
# here is the magic, this get the type of a 'normal method' of a class
method = type(obj.func)
# this bounds the method to the object, so self is passed by default
obj.func = method(decorated_func, obj)
我认为这是在运行时装饰对象方法的最佳方式,不过如果能找到一种方法直接调用method()
,而不需要那行method = type(obj.func)
就更好了。