Python 装饰器|Decorators
python的装饰器(Decorators)定义:具有执行环境的函数,其满足三个条件:
- 外部函数中定义一个内部函数
- 内部函数中使用外部函数的局部变量
- 外部函数将内部函数作为返回值返回
此时的内部函数就叫闭包(Closure), 装饰器允许我们包装内部的函数以扩展函数的行为,而不必对内部函数修改。
-
装饰器函数(使用装饰器和取消装饰器)
例子:输出函数执行时间的装饰器。
1
2
3
4
5
6
7
8
9
10
11def record_time(func):
"""自定义装饰函数的装饰器"""
def wrapper(*args, **kwargs):
start = time()
result = func(*args, **kwargs)
print(f'{func.__name__}: {time() - start}秒')
return result
return wrapper如果装饰器不希望跟
print函数耦合,可以编写带参数的装饰器。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19from functools import wraps
from time import time
def record(output):
"""自定义带参数的装饰器"""
def decorate(func):
def wrapper(*args, **kwargs):
start = time()
result = func(*args, **kwargs)
output(func.__name__, time() - start)
return result
return wrapper
return decorate1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20from functools import wraps
from time import time
class Record():
"""自定义装饰器类(通过__call__魔术方法使得对象可以当成函数调用)"""
def __init__(self, output):
self.output = output
def __call__(self, func):
def wrapper(*args, **kwargs):
start = time()
result = func(*args, **kwargs)
self.output(func.__name__, time() - start)
return result
return wrapper说明:由于对带装饰功能的函数添加了@wraps装饰器,可以通过
func.__wrapped__方式获得被装饰之前的函数或类来取消装饰器的作用。例子:用装饰器来实现单例模式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20from functools import wraps
def singleton(cls):
"""装饰类的装饰器"""
instances = {}
def wrapper(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
class President():
"""总统(单例类)"""
pass说明:上面的代码中用到了闭包(closure),不知道你是否已经意识到了。还没有一个小问题就是,上面的代码并没有实现线程安全的单例,如果要实现线程安全的单例应该怎么做呢?
提示:代码用到了
with上下文语法来进行锁操作,因为锁对象本身就是上下文管理器对象(支持__enter__和__exit__魔术方法)。在wrapper函数中,我们先做了一次不带锁的检查,然后再做带锁的检查,这样做比直接加锁检查性能要更好,如果对象已经创建就没有必须再去加锁而是直接返回该对象就可以了。
1 | from functools import wraps |
fgd
1 |
|
Closure
1 | def hello(): |
1 | def hello(msg1): |
1 | def hello(): |
Decorators
从最简单的helloword程序说起, 我们先定义一个hello world函数,和另一个叫flower的函数,再把这个hello world函数传给那个flower函数:
1 | def hello(): |
1 | def hello(): |
我们传入一个函数,
1 | def flower(func): |
1 | def flower(func): |
我们再来看一个复杂的,
1 | def flower(message): |
如果多个函数被两个装饰器装饰时就报错,因为两个函数名一样,第二个函数再去装饰的话就出错, 一般的话,引入@functools.wraps(func)可避免此问题。
1 | import functools |
Functools
-
functool.update_wrapper:将被包装函数的
__name__等属性,拷贝到新的函数中去。1
2
3
4
5
6
7
8
9
10
11
12
13
14from functools import update_wrapper
def wrap2(func):
def inner(*args):
return func(*args)
return update_wrapper(inner, func)
def demo():
print('hello world')
demo()
# hello world
print(demo.__name__)
# demo -
functool.wraps: 在
update_wrapper上进行一个包装, 拷贝被装饰函数的__name__。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17from functools import wraps
def wrap2(func):
def inner(*args):
print(func.__name__)
return func(*args)
return inner
def demo():
print('hello world')
demo()
# demo
# hello world
print(demo.__name__)
# demo -
functools.lru_cache:将一个函数的返回值快速地缓存或取消缓存。
该装饰器会将不同的调用结果缓存在内存中,因此需要注意内存占用问题。
1
2
3
4
5
6
7
8
9from functools import lru_cache
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
print([fib(n) for n in range(10)])
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
fib.cache_clear() # 清空缓存 -
functools.singledispatch: 用于实现泛型函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47from functools import singledispatch
from decimal import Decimal
def fun(arg, verbose=False):
if verbose:
print("Let me just say,", end=" ")
print(arg)
def _(arg, verbose=False):
if verbose:
print("Strength in numbers, eh?", end=" ")
print(arg)
def _(arg, verbose=False):
if verbose:
print("Enumerate this:")
for i, elem in enumerate(arg):
print(i, elem)
def fun_num(arg, verbose=False):
if verbose:
print("Half of your number:", end=" ")
print(arg / 2)
fun('hello world!', True)
# Let me just say, hello world!
fun(123, True)
# Strength in numbers, eh? 123
fun(['a','b','c'], True)
# Enumerate this:
# 0 a
# 1 b
# 2 c
fun(1.23, True)
# Half of your number: 0.615
print(fun.registry) # 所有的泛型函数
# {<class 'object'>: <function fun at 0x0000000005069F78>, <class 'int'>: <function _ at 0x0000000005098558>, \
# <class 'list'>: <function _ at 0x0000000005098678>, <class 'decimal.Decimal'>: <function fun_num at 0x0000000005098828>,\
# <class 'float'>: <function fun_num at 0x0000000005098828>}
print(fun.registry[float]) # 获取float的泛型函数
# <function fun_num at 0x0000000005098E58>
fedf
1 | """ |





