Python3 装饰器
Python3 学习笔记 · 整理自菜鸟教程
一句话
装饰器是一个接收函数、返回函数的函数,用 @ 语法叠在函数定义上,在不改动原函数代码的前提下给它增加功能。
要点
- 装饰器本质是
f = decorator(f) 的语法糖。
- 用
@decorator 放在被装饰函数定义上一行。
- 内层用
*args, **kwargs 接收任意参数,保证通用。
- 用
functools.wraps 保留原函数的名字和文档,否则变成包装函数的信息。
- 装饰器可带参数:外层再包一层,
@deco(参数) 返回真正的装饰器。
- 一个函数可叠多个装饰器,从下往上依次生效。
- 常见用途:日志、计时、权限校验、缓存、重试。
语法 / 常用方法
| 语法/方法 |
说明 |
示例 |
@deco |
装饰器语法糖 |
@timer |
def deco(f): def w(*a, **k): ... |
基本结构 |
返回内层 w |
@functools.wraps(f) |
保留原函数元信息 |
加在内层 w 上 |
@deco(arg) |
带参数的装饰器 |
需再包一层 |
f(*args, **kwargs) |
内层调用原函数并透传参数 |
return f(*args, **kwargs) |
多个 @ 叠加 |
由下往上执行 |
先下的后上的 |
functools.lru_cache |
内置缓存装饰器 |
@lru_cache(maxsize=None) |
代码示例
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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| import functools import time
def timer(func): @functools.wraps(func) def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) print(f"{func.__name__} 耗时 {time.time()-start:.4f}s") return result return wrapper
@timer def slow(n): time.sleep(0.1) return n * 2
slow(5)
def repeat(times): def deco(func): @functools.wraps(func) def wrapper(*args, **kwargs): for _ in range(times): result = func(*args, **kwargs) return result return wrapper return deco
@repeat(3) def say(msg): print(msg)
say("hi")
def bold(f): def w(*a, **k): return "<b>" + f(*a, **k) + "</b>" return w
def italic(f): def w(*a, **k): return "<i>" + f(*a, **k) + "</i>" return w
@bold @italic def text(): return "hello"
print(text())
@functools.lru_cache(maxsize=None) def fib(n): return n if n < 2 else fib(n-1) + fib(n-2)
print(fib(30))
|
易错点
- 不写
@functools.wraps,被装饰函数的名字会变成 wrapper,文档也丢失。
- 内层忘了
return f(*args, **kwargs),原函数返回值丢失。
- 带参数的装饰器必须再套一层函数,否则参数会被当成被装饰函数。
- 叠多个装饰器顺序容易搞反:最靠近函数的最先执行。
- 装饰器在定义时就执行(
@ 那行立即调用一次),不是调用函数时才执行。
- 被
lru_cache 装饰的函数参数必须可哈希,列表/字典作参数会报错。