python装饰器堆叠

时间:2014-10-21 08:17:50

标签: python functional-programming decorator

我一直在努力更好地理解装饰器和封闭装置。

我正在尝试修饰函数来实现:

  • 记住之前传递的值,
  • 计算调用该函数的次数。

我想使用两个单独的装饰器 - 用于科学:)

所以我设法创建了这个工作代码(我使用了一些代码片段 - 我承认)

class countcalls(object):
    "Decorator that keeps track of the number of times a function is called."

    __instances = {}

    def __init__(self, f):
        self.__f = f
        self.__numcalls = 0
        countcalls.__instances[f] = self

    def __call__(self, *args, **kwargs):
        self.__numcalls += 1
        return self.__f(*args, **kwargs)

    def count(self):
        "Return the number of times the function f was called."
        return countcalls.__instances[self.__f].__numcalls

    @staticmethod
    def counts():
        "Return a dict of {function: # of calls} for all registered functions."
        return dict([(f.__name__, countcalls.__instances[f].__numcalls) for f in countcalls.__instances])

def wrapper(x):
    past=[]
    @countcalls
    def inner(y):
        print x 
        print inner.count()
        past.append(y)
        print past

    return inner

def main():
    foo = wrapper("some constant")

    foo(5)
    foo("something")


if __name__ == '__main__':
    main()

输出:

some constant
1
[5]
some constant
2
[5, 'something']  

现在我想将memoize函数更改为一个整洁的pythonic装饰器。这是我到目前为止所提出的:

class countcalls(object):
    "Decorator that keeps track of the number of times a function is called."

    __instances = {}

    def __init__(self, f):
        self.__f = f
        self.__numcalls = 0
        countcalls.__instances[f] = self

    def __call__(self, *args, **kwargs):
        self.__numcalls += 1
        return self.__f(*args, **kwargs)

    def count(self):
        "Return the number of times the function f was called."
        return countcalls.__instances[self.__f].__numcalls

    @staticmethod
    def counts():
        "Return a dict of {function: # of calls} for all registered functions."
        return dict([(f.__name__, countcalls.__instances[f].__numcalls) for f in countcalls.__instances])


class memoize(object):
    past=[]

    def __init__(self, f):
        past = []
        self.__f = f

    def __call__(self, *args, **kwargs):
        self.past.append(*args)

        return self.__f(*args, **kwargs)

    def showPast(self):
        print self.past


@memoize
@countcalls
def dosth(url):
    print dosth._memoize__f.count()  ## <-- this is so UGLY
    dosth.showPast()

def main():
    dosth("one")
    dosth("two")

if __name__ == '__main__':
    main()

这是输出:

1
['one']
2
['one', 'two']

如何摆脱丑陋的&#34; line(print dosth._memoize__f.count())?换句话说 - 如何直接调用堆叠装饰器的方法? (没有向装饰器添加一个方法来调用其他装饰器的方法 - 这不是我的观点)

1 个答案:

答案 0 :(得分:0)

如果您想要访问特定的装饰器结果,您仍然需要打开装饰器,但是默认情况下&#39;这个属性是__wrapped__。 {3}的Python 3版本为您设置了此属性(通过@functools.wraps() decorator,从Python 3.4开始),但您可以在自己的装饰器中手动执行相同的操作:

class memoize(object):
    past=[]

    def __init__(self, f):
        past = []
        self.__wrapped__ = self.__f = f

现在您可以使用以下命令访问包装的可调用对象:

dosth.__wrapper__.count()

标准的 Pythonic解包装饰器的方式。