【问题标题】:Decorating Instance Methods in Python在 Python 中装饰实例方法
【发布时间】:2010-12-12 22:12:14
【问题描述】:

这是我正在尝试做的事情的要点。我有一个对象列表,我知道它们有一个实例方法,如下所示:

def render(self, name, value, attrs)
   # Renders a widget...

我想(基本上)在运行时装饰这些函数,因为我正在遍历对象列表。这样他们的渲染函数就变成了这样:

def render(self, name, value, attrs)
   self.attrs=attrs
   # Renders a widget...

两个警告:

  1. render 函数是 django 的一部分。我不能在他们的库中放置一个装饰器(我可以,但是我必须维护和迁移这个更改)。
  2. 这是一个实例方法。

这里有一个例子: http://wiki.python.org/moin/PythonDecoratorLibrary

展示了如何向类添加新的实例方法。这里的不同之处在于,在我记住了 attrs 参数之后,我想回到原来的方法。

【问题讨论】:

    标签: python django class decorator instance


    【解决方案1】:
    def decorate_method(f):
      def wrapper(self, name, value, attrs):
        self.attrs = attrs
        return f(self, name, value, attrs)
      return wrapper
    
    def decorate_class(c):
      for n in dir(c):
        f = getattr(c, n)
        if hasattr(f, 'im_func'):
          setattr(c, n, decorate_method(f.im_func))
    

    您可能需要一些其他测试来跳过具有不同签名的方法,但除此之外,decorate_class(whatever) 应该在任何给定类whatever 上执行您想要的操作。

    【讨论】:

    • 您是在头脑中编写的代码,不是吗? :)
    • @Alex:是的,查看修订历史。 setattr 被传递给 f 而不是 n 作为它的第二个参数。
    • (最近我在你发布的另一个代码 sn-p 中发现了一个错字,stackoverflow.com/questions/1653500/…。我必须说,我自己通常没有足够的信心在没有先在解释器中测试代码的情况下发布代码: )
    【解决方案2】:

    “经典”方式是子类化。这样你就不必和其他人的课程混在一起了。

    class someclass(object):
        def render(self, name, value, attrs):
            print hasattr(self, 'attrs')
    
    class my_render(object):
        def render(self, name, value, attrs):
            self.attrs = attrs # kind of decorating the function here
            return super(my_render, self).render(name, value, attrs)
    
    class my_class(my_render, someclass): 
        pass    
    
    someclass().render(1,2,3) # -> False
    my_class().render(1,2,3) # -> True
    

    MI 的原因是所有类都可以继承自my_render。我喜欢 mixin 的概念 ;-)

    class my_otherclass(my_render, someotherclass): pass
    class my_thirdclass(my_render, thirdclass): pass
    
    # or less explicit
    classlist = [ someclass, someotherclass ]
    newclasses = [ type('my_'+cls.__name__, (my_render,cls), {}) for cls in classlist ]
    

    【讨论】:

    • 有趣的是,您选择使用 MI 作为一个简单的示例,而不是让 my_render 从某个类继承。
    • 是的,这通常是我的做法,但我希望能够简单地利用装饰,这样我就不必对 Django 提供的所有各种类型的字段进行子类化和 MI/decorate 无论它是 InputField、DateTimeField 等。Django 在渲染字段时做了很多黑魔法。我经历了一次冒险,发现每个字段在渲染之前都被包裹在“BoundField”中......
    猜你喜欢
    • 2013-06-23
    • 1970-01-01
    • 2013-02-12
    • 2011-03-23
    • 2019-01-03
    • 2019-02-03
    相关资源
    最近更新 更多