【问题标题】:How to decorate an object method?如何装饰一个对象方法?
【发布时间】:2011-05-10 13:17:56
【问题描述】:

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

以及为什么它不起作用: 由于我构建应用程序的方式,它不起作用,当我的 Decorator 类中不存在的方法被调用时,感觉回到了装饰类的实现,问题是应用程序总是开始调用方法 @987654325 @ 不需要装饰,所以使用了未装饰的回退,并且从未装饰的表单内部它总是调用 undecorated 方法,我需要的是替换对象中的方法,而不是代理它:

# 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。

【问题讨论】:

  • Space_C0wb0y - 我对__getattr__ 有疑问,A 类是由 B 类修饰的类,A 有一个调用方法 do() 的方法 run(),B 类修饰了方法 do(),但是因为我在 run() 中使用递归并且我从类 A 方法的 run() 启动它,所以它调用它自己的 do() 版本,而不是修饰的版本。
  • 你的问题可以做一些澄清,但如果我没看错,你想“破解”另一个模块中的一些已经编写的代码,以便它实例化一个修改过的对象而不是它通常使用。如果是这样,this answer 会告诉你一种方法。

标签: python python-3.x runtime decorator


【解决方案1】:

问题是我不能使用 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) 这一行

【讨论】:

    【解决方案2】:

    您可能希望使用__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)
    

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 我不明白什么是不正确的,这就是我所做的。我知道表达式@decofunc = deco(func) 的语法糖,但它对methods 的作用不同,如果在定义类时修饰方法,它将创建一个method,在定义类和修饰方法之后试试这个:type(class().method)。如果你之后装饰调用该函数,它会创建一个function,尝试:type(deco(class().method))
      【解决方案4】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-08-14
        • 1970-01-01
        • 1970-01-01
        • 2017-01-24
        • 2010-11-28
        • 2021-10-24
        • 2020-08-12
        • 1970-01-01
        相关资源
        最近更新 更多