1. 将装饰器定义为类

  1. # 你想使用一个装饰器去包装函数,但是希望返回一个可调用的实例。 你需要让你的装饰器可以同时工作在类定义的内部和外部。
  2. # 为了将装饰器定义成一个实例,你需要确保它实现了 __call__() 和 __get__() 方法。
  3. import types
  4. from functools import wraps
  5. class Profiled:
  6. def __init__(self, func):
  7. wraps(func)(self)
  8. self.ncalls = 0
  9. def __call__(self, *args, **kwargs):
  10. self.ncalls += 1
  11. return self.__wrapped__(*args, **kwargs)
  12. def __get__(self, instance, cls):
  13. if instance is None:
  14. return self
  15. else:
  16. return types.MethodType(self, instance)
  17. @Profiled
  18. def add(x, y):
  19. return x + y
  20. class Spam:
  21. @Profiled
  22. def bar(self, x):
  23. print(self, x)
  24. print(add(2, 3))
  25. print(add(4, 5))
  26. print(add.ncalls)
  27. s = Spam()
  28. s.bar(5)
  29. s.bar(1)
  30. s.bar(3)
  31. print(Spam.bar.ncalls)
  1. 5
  2. 9
  3. 2
  4. <__main__.Spam object at 0x0000020AEEBF9B70> 5
  5. <__main__.Spam object at 0x0000020AEEBF9B70> 1
  6. <__main__.Spam object at 0x0000020AEEBF9B70> 3
  7. 3

3. * 装饰器统计函数调用次数

  1. from functools import wraps
  2. def profiled(func):
  3. ncalls = 0
  4. @wraps(func)
  5. def wrapper(*args, **kwargs):
  6. nonlocal ncalls
  7. ncalls += 1
  8. return func(*args, **kwargs)
  9. wrapper.ncalls = lambda: ncalls
  10. return wrapper
  11. @profiled
  12. def add(x, y):
  13. return x + y
  14. print(add(1, 2))
  15. print(add(3, 4))
  16. print(add.ncalls())
  1. 3
  2. 7
  3. 2