【问题标题】:add a decorate function to a class为类添加装饰功能
【发布时间】:2011-01-30 21:16:46
【问题描述】:

我有一个装饰函数(简化版):

class Memoize:
    def __init__(self, function):
        self.function = function
        self.memoized = {}
    def __call__(self, *args, **kwds):
        hash = args
        try:
            return self.memoized[hash]
        except KeyError:
            self.memoized[hash] = self.function(*args)
            return self.memoized[hash]


@Memoize
def _DrawPlot(self, options):
    do something...

现在我想将此方法添加到预先存在的类中。

ROOT.TChain.DrawPlot = _DrawPlot

当我调用这个方法时:

chain = TChain()
chain.DrawPlot(opts)

我明白了:

self.memoized[hash] = self.function(*args)
TypeError: _DrawPlot() takes exactly 2 arguments (1 given)

为什么不自我传播?

【问题讨论】:

  • 总是从 object 继承而不是什么都没有,这样你就可以使用新样式的类了。
  • 如果您有任何选择,请不要使用首字母大写的方法命名。使用 _draw_plot(PEP 8 推荐)或 _drawPlot 之类的名称。
  • 我的回答具有误导性,所以它得到了印章。感谢迈克的反馈!

标签: python class decorator descriptor


【解决方案1】:

问题是您已经定义了自己的可调用类,然后尝试将其用作方法。当您将函数用作属性时,将函数作为属性访问时会调用它的__get__ 方法来返回函数本身以外的东西——绑定方法。如果你有自己的类而不定义__get__,它只会返回你的实例而不隐式传递self

如果您不熟悉描述符,请在 http://docs.python.org/reference/datamodel.html#descriptors 上进行一些解释。 __get____set____delete__ 方法改变了与对象交互作为属性的工作方式。


您可以将memoize 实现为函数,并使用函数已有的内置__get__ 魔法

import functools

def memoize(f):
    @functools.wraps(f)
    def memoized(*args, _cache={}): 
        # This abuses the normally-unwanted behaviour of mutable default arguments.
        if args not in _cache:
            _cache[args] = f(*args)
        return _cache[args]
    return memoized

或者通过修改你的类

import functools

class Memoize(object): #inherit object
    def __init__(self, function):
        self.function = function
        self.memoized = {}
    def __call__(self, *args): #don't accept kwargs you don't want.
        # I removed "hash = args" because it shadowed a builtin function and 
        # because it was untrue--it wasn't a hash, it was something you intended for
        # Python to hash for you.
        try:
            return self.memoized[args]
        except KeyError:
            self.memoized[args] = self.function(*args)
            return self.memoized[args]
    def __get__(self, obj, type):
        if obj is None: #We looked up on the class
            return self

        return functools.partial(self, obj)

请注意,如果您传入的任何参数是可变的(嗯,从技术上讲,不可散列),这两个都会阻塞。这可能适合您的情况,但您可能还想处理args 不可散列的情况。

【讨论】:

  • 我更喜欢第二个版本,因为我可以控制缓存,我可以创建多个 Memorize 对象和不同的缓存用于不同的功能。我正在使用比hash = args 更复杂的东西,因为我需要处理可变对象。正如你所说,“哈希”不是一个很好的名字。
  • @wiso,第一个代码示例将为您记忆的每个函数提供不同的缓存。尝试在记忆中处理可变类型时要小心;出于充分的理由,它们不可散列。您必须了解函数 being 记忆的工作方式,才能知道它是否有问题。
  • 抱歉,您的解决方案不起作用:@Memoize def function(self, x): print self return x*x class my_class: pass my_class.do = function c = my_class() print c .do(2) TypeError: function() 正好接受 2 个参数(1 个给定)
  • @wiso,对不起,我应该对此进行测试。我记得这是如何工作的有点错误并修复它。对于那个很抱歉。 codepad.org/jXkD8QHC
猜你喜欢
  • 2010-11-22
  • 1970-01-01
  • 1970-01-01
  • 2011-07-29
  • 2011-02-14
  • 2021-11-24
  • 1970-01-01
  • 2011-07-25
  • 2022-08-05
相关资源
最近更新 更多