实现一个简单的装饰器
一句话总结:把别装饰的函数替换成新函数,二者接受相同的参数,一般情况下返回被装饰的函数本该返回的值,同时还会做一些额外操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import time
def clock(func): def clocked(*args): begin = time.perf_counter() result = func(*args) elapsed = time.perf_counter() - begin name = func.__name__ arg_str = ','.join(repr(arg) for arg in args) print('[%0.8fs] %s(%s) -> %r' % (elapsed, name, arg_str, result)) return result return clocked
|
使用装饰器:
1 2 3 4 5 6 7 8 9 10 11
| import time from clockdeco import clock
@clock def snooze(seconds): time.sleep(seconds)
@clock def factorial(n): return 1 if n < 2 else n*factorial(n-1)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| print('*' * 40, 'Calling snooze(.123)') snooze(.123) print('*' * 40, 'Calling factorial(6)')
print('6! = ', factorial(6))
``` **************************************** Calling snooze(.123) [0.12749738s] snooze(0.123) -> None **************************************** Calling factorial(6) [0.00000154s] factorial(1) -> 1 [0.00010962s] factorial(2) -> 2 [0.00018708s] factorial(3) -> 6 [0.00026196s] factorial(4) -> 24 [0.00049425s] factorial(5) -> 120 [0.00076513s] factorial(6) -> 720 6! = 720 ```
|
但此时如果访问被装饰函数的__name__
和__doc__
时就被clocked遮盖了
1 2
| snooze.__name__ 'clocked'
|
可以用functools模块中的functools.wraps来构建行为良好的解释器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import time import functools
def clock(func): @functools.wraps(func) def clocked(*args, **kwargs): begin = time.time() result = func(*args, **kwargs) elapsed = time.time() - begin name = func.__name__ arg_lst = [] if args: arg_lst.append(','.join(repr(arg) for arg in args)) if kwargs: pairs = ['%s=%r' % (k, w) for k, w in sorted(kwargs.items())] arg_lst.append(','.join(pairs)) arg_str = ','.join(arg_lst) print('[%0.8fs] %s(%s) -> %r' % (elapsed, name, arg_str, result)) return result return clocked
|
叠放装饰器
1 2 3 4
| @d1 @d2 def f(): print('f')
|
等同于:
1 2 3
| def f(): print('f') f() = d1(d2(f))
|