为类添加一个decorate函数

时间:2010-03-23 16:05:54

标签: python class decorator descriptor

我有一个装饰功能(简化版):

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)

为什么不传播自我?

1 个答案:

答案 0 :(得分:3)

问题是您已经定义了自己的可调用类,然后尝试将其用作方法。当您使用函数作为属性时,作为属性访问函数会将其__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不可用的情况。