【问题标题】:python decorators stackingpython装饰器堆叠
【发布时间】:2014-12-16 09:31:57
【问题描述】:

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

我正在尝试实现的装饰功能:

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

我想使用两个独立的装饰器来制作它 - 用于科学:)

所以我设法创建了这个工作代码(我使用了一些 sn-p 进行计数 - 我承认)

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']

如何摆脱“丑陋”的行(print dosth._memoize__f.count())?换句话说 - 我怎样才能直接调用堆叠装饰器的方法? (不向装饰器添加方法来调用其他装饰器的方法 - 这不是我的意思)

【问题讨论】:

  • 你为什么要从函数中访问装饰器根本
  • 我认为在这种情况下,它是一种计算函数被调用次数并根据它修改函数行为的好方法 - 如果我想访问外部的计数属性功能的 - 我仍然需要使用 dosth._memoize__f.count()
  • 如果你把计数器放在 外面 记忆器就不会了。

标签: python functional-programming decorator


【解决方案1】:

如果你想访问特定的装饰器结果,你仍然需要解开装饰器,但是'default'属性是__wrapped__@functools.wraps() decorator 的 Python 3 版本为您设置此属性(通过 functools.update_wrapper() utility function,从 Python 3.4 开始),但您可以在自己的装饰器中手动执行相同操作:

class memoize(object):
    past=[]

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

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

dosth.__wrapper__.count()

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-12
    • 1970-01-01
    • 2020-05-15
    • 1970-01-01
    • 2013-08-07
    • 2014-01-23
    • 1970-01-01
    相关资源
    最近更新 更多