我正在阅读有关装饰器的内容,并尝试将这两个示例混合在一起,并使它们成为类装饰器而不是常规函数。第一个只允许你为每个参数运行一次函数,第二个只计算你运行该函数的次数。它们都可以很好地分离,但是当我尝试用它们同时装饰一个简单的函数时它会失败...或者不会真的失败但会打印出意想不到的错误结果。我做了一些阅读,发现functools模块可以提供帮助,但我不确定如何。
from functools import update_wrapper
class Memoize:
def __init__(self, func):
self.func = func
self.memo = dict()
update_wrapper(self, func)
def __call__(self, *args):
if args not in self.memo:
self.memo[args] = self.func(args)
else:
print("cls decorator. You have printed this before")
return self.memo[args]
class CallCounter:
def __init__(self, func):
self.func = func
self.calls = 0
self.__name__ = func.__name__
update_wrapper(self, func)
def __call__(self, *args, **kwargs):
self.calls += 1
return self.func(*args, **kwargs)
@Memoize
@CallCounter
def doubleprint(x):
for elem in x:
print(elem + " " + elem)
doubleprint('Hello')
doubleprint('Hello')
doubleprint('Hello')
doubleprint('Hello')
doubleprint('Bye')
print(doubleprint.calls)
doubleprint('Hello')
doubleprint('Hello')
doubleprint('Hello')
doubleprint('Hello')
doubleprint('Bye')
print(doubleprint.calls)
答案 0 :(得分:1)
默认情况下,update_wrapper
会从包装的类中更新__dict__
。因此func
中的Memoize
被func
中的CallCounter
覆盖,这意味着Memoize
正在直接调用您的doubleprint()
函数并且从不调用CallCounter
{1}}。
class Memoize:
def __init__(self, func):
self.func = func
print(type(self.func)) # <class '__main__.CallCounter'>
self.memo = dict()
update_wrapper(self, func)
print(type(self.func)) # <class 'function'>
您可以通过执行以下操作来解决此问题:
update_wrapper(self, func, updated=[])
不会将__dict__
从CallCounter
复制到Memoize
个实例,但仍会复制__name__, __doc__
等。
要访问CallCounter
课程,您需要:
print(doubleprint.__wrapped__.calls)
但是你需要上面的修复,否则它将始终打印0
(因为它永远不会被调用)。