2017-08-15 92 views
3

我在阅读关于装饰器,并试图混合这两个例子,并使他们类装饰器,而不是常规功能。第一个函数只允许你为每个参数运行一次函数,第二个函数计算你运行该函数的次数。他们都工作很好分开,但当我试图装饰一个简单的功能与两个在同一时间失败......或不真正失败,但打印一个意想不到的错误结果。我做了一些阅读,发现functools模块可以帮助,但我不知道如何。如何将这两个类装饰器与functools.update_wrapper一起使用?

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) 

回答

1

默认情况下update_wrapper更新从包裹类__dict__。因此,Memoize中的func正被func中的CallCounter覆盖,这意味着Memoize正在直接调用您的doubleprint()函数,并且从不呼叫CallCounter

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=[]) 

,不会从CallCounter复制__dict__Memoize实例,但仍会复制__name__, __doc__

要访问CallCounter类你会:

print(doubleprint.__wrapped__.calls) 

但是,您需要上述修正或者总是打印0(因为它从未被调用过)。

相关问题