0%

Python知识点-装饰器

实现一个简单的装饰器

一句话总结:把别装饰的函数替换成新函数,二者接受相同的参数,一般情况下返回被装饰的函数本该返回的值,同时还会做一些额外操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 一个简单的装饰器,输出函数的运行时间
import time

def clock(func):
def clocked(*args): #*args的用法:当传入的参数个数未知,且不需要知道参数名称时。
#**args的用法:当传入的参数个数未知,但需要知道参数的名称时(立马想到了字典,即键值对)
begin = time.perf_counter() # 调用func时的时间
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))
# func运行后,输出格式是[时间] 函数名(传入参数)-> 函数结果
return result # 记得返回结果!
return clocked # 被装饰函数已经被换成了一个新的函数

使用装饰器:

1
2
3
4
5
6
7
8
9
10
11
import time
from clockdeco import clock # 直接import

@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): #*args的用法:当传入的参数个数未知,且不需要知道参数名称时。
#**args的用法:当传入的参数个数未知,但需要知道参数的名称时(立马想到了字典,即键值对)
begin = time.time() # 调用func时的时间
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))
# func运行后,输出格式是[时间] 函数名(传入参数)-> 函数结果
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))