如何装饰对象方法?

2 投票
4 回答
3126 浏览
提问于 2025-04-16 17:21

我需要给一个对象的方法加装饰。这个装饰需要在运行时进行,因为应用在对象上的装饰取决于用户在调用程序时提供的参数(通过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 个回答

0

你可能想用 __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)
0

我需要给一个对象的方法加装饰。这个装饰需要在运行时进行,因为应用在对象上的装饰取决于用户在调用程序时提供的参数(通过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)

2

问题在于我不能把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)就更好了。

撰写回答