关于python:如何修饰一个对象方法?

How to decorate an object method?

我需要装饰一个物体的方法。它需要在运行时进行,因为应用于对象的装饰器依赖于用户在调用程序时提供的参数(argv提供的参数),因此同一个对象可以装饰3次、2次或根本不装饰。

这里是一些上下文,程序是一个解谜者,主要行为是自动找到解谜的解决方案,我的意思是自动的,不需要用户干预。这里是装饰的地方,我想画一张执行过程中发生的事情的图表,但我只想在使用--draw-graph旗时这样做。

以下是我的尝试:

1
2
3
4
5
6
7
8
9
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)

为什么它不起作用:它不起作用,因为我构建应用程序的方式,当调用decorator类中不存在的方法时,它感觉回到了decorated类的实现中,问题是应用程序总是开始调用不需要修饰的方法run,因此使用了未修饰的fall back,并且在未修饰的窗体内,它始终称为未修饰的方法,我需要的是从对象中替换该方法,而不是代理它:

1
2
3
4
5
6
7
8
9
10
# 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,因为参数数目错误导致了类型错误。


问题是我不能用func(self)作为方法。原因是setAttr()方法没有绑定函数,并且函数的行为类似于一个静态方法(而不是类方法),多亏了Python的内省性,我能够想出这个解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
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)行。


"它需要在运行时,因为应用于对象的装饰器取决于用户在调用程序时提供的参数。"

不使用装修工。装饰器只是包装器的语法支持,您也可以使用普通的函数/方法调用。


I need to decorate a object's method. It needs to be at runtime because the decorators applied on the object depends on the arguments that the user gave when calling the program (arguments supplied with argv), so a same object could be decorated 3 times, 2 times, or not be decorated at all.

不幸的是,上面的内容是不正确的,您试图做的是不必要的。您可以在运行时这样做。例子:

1
2
3
4
5
6
7
8
9
10
import sys
args = sys.argv[1:]

class MyClass(object):
    pass

if args[0]=='--decorateWithFoo':
    MyClass = decoratorFoo(MyClass)
if args[1]=='--decorateWithBar'
    MyClass = decoratorBar(MyClass)

句法:

1
2
@deco
define something

与以下内容相同:

1
2
define something
something = deco(something)

你也可以做一家装饰工厂。


您可能希望使用__getattribute__,而不是__getattr__(后者仅在"标准"查找失败时调用):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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)