装饰器
装饰器的本质是一个函数,它接收另一个函数作为参数(被装饰函数),返回一个新的函数对象。
简单说:
“装饰器 = 一个接收函数并返回函数的函数。”
例子:
1 2 3 4 5 6 7 8 9 10 11 12
| def decorator(func): def wrapper(): print("函数开始执行") func() print("函数执行结束") return wrapper
@decorator def say_hello(): print("Hello!")
say_hello()
|
输出:
等价于:
1 2
| say_hello = decorator(say_hello) say_hello()
|
@decorator 只是语法糖。
- 装饰器的返回值会替换原函数。
- 本质是:
func = decorator(func)。
工作机制
1 2 3
| @装饰器名 def 函数名(...): ...
|
等价于:
执行顺序:
- Python 解释器加载函数定义;
- 看到
@装饰器 标记;
- 立即调用装饰器,将函数传入;
- 把装饰器返回的新函数对象重新绑定到原函数名上。
为什么需要装饰器?
装饰器常用于在函数执行前后插入通用逻辑,比如:
| 场景 |
功能 |
| 日志记录 |
打印函数调用信息 |
| 性能分析 |
统计函数执行时间 |
| 权限控制 |
检查用户是否有权限 |
| 缓存机制 |
保存上次执行结果 |
| 异常处理 |
自动捕获并记录错误 |
这样可以让逻辑“可插拔”,而无需修改业务代码。
装饰器类型
带参函数的装饰器
上面的例子只适用于无参函数。如果被装饰函数有参数,装饰器应使用 *args, **kwargs:
1 2 3 4 5 6 7 8 9 10 11 12 13
| def logger(func): def wrapper(*args, **kwargs): print(f"调用函数 {func.__name__}(),参数:{args}, {kwargs}") result = func(*args, **kwargs) print(f"函数 {func.__name__}() 执行完毕") return result return wrapper
@logger def add(a, b): return a + b
add(3, 5)
|
输出:
1 2
| 调用函数 add(),参数:(3, 5), {} 函数 add() 执行完毕
|
带返回值的装饰器
如果原函数有返回值,装饰器要记得返回这个结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| def timer(func): import time def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"{func.__name__} 运行耗时: {end - start:.4f}秒") return result return wrapper
@timer def slow_add(a, b): import time; time.sleep(1) return a + b
print(slow_add(3, 7))
|
输出:
1 2
| slow_add 运行耗时: 1.0005秒 10
|
叠加多个装饰器
可以同时使用多个装饰器。执行顺序是自下而上(离函数最近的先执行):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| def deco1(func): def wrapper(): print("deco1 开始") func() print("deco1 结束") return wrapper
def deco2(func): def wrapper(): print("deco2 开始") func() print("deco2 结束") return wrapper
@deco1 @deco2 def greet(): print("Hello")
greet()
|
等价于:
1
| greet = deco1(deco2(greet))
|
输出:
1 2 3 4 5
| deco1 开始 deco2 开始 Hello deco2 结束 deco1 结束
|
带参数的装饰器
有时希望给装饰器本身传参数。
比如:
1 2 3
| @repeat(3) def hello(): print("hi")
|
这时装饰器要多包一层函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def repeat(n): def decorator(func): def wrapper(*args, **kwargs): for i in range(n): print(f"第 {i+1} 次调用") func(*args, **kwargs) return wrapper return decorator
@repeat(3) def hello(): print("Hello!")
hello()
|
输出:
1 2 3 4 5 6
| 第 1 次调用 Hello! 第 2 次调用 Hello! 第 3 次调用 Hello!
|
例子结构如下:
1 2
| repeat(n) → 返回 decorator decorator(func) → 返回 wrapper
|
functools.wraps在实战中非常重要,当你装饰一个函数时,原函数的元信息(__name__, __doc__, __annotations__ 等)会被替换为 wrapper 的。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13
| def deco(func): def wrapper(): """Wrapper docstring""" pass return wrapper
@deco def func(): """Original docstring""" pass
print(func.__name__) print(func.__doc__)
|
这会破坏调试与文档生成。
✅ 解决办法:
使用 functools.wraps 来复制原函数的元信息:
1 2 3 4 5 6 7 8 9 10
| from functools import wraps
def deco(func): @wraps(func) def wrapper(*args, **kwargs): print("执行前") result = func(*args, **kwargs) print("执行后") return result return wrapper
|
这样 func.__name__ 和 func.__doc__ 会保留。
类装饰器
除了函数,装饰器也可以用类来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class Decorator: def __init__(self, func): self.func = func
def __call__(self, *args, **kwargs): print("调用前") result = self.func(*args, **kwargs) print("调用后") return result
@Decorator def greet(name): print(f"Hello, {name}")
greet("Tom")
|
执行时,greet 被替换成一个 Decorator 实例,调用时执行 __call__()。
实战场景示例
1️⃣ 日志装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13
| from functools import wraps
def log_call(func): @wraps(func) def wrapper(*args, **kwargs): print(f"[LOG] 调用 {func.__name__}(),参数:{args}, {kwargs}") return func(*args, **kwargs) return wrapper
@log_call def add(a, b): return a + b
add(2, 5)
|
2️⃣ 权限检查装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13
| def require_admin(func): @wraps(func) def wrapper(user, *args, **kwargs): if not user.get("is_admin"): raise PermissionError("没有管理员权限") return func(user, *args, **kwargs) return wrapper
@require_admin def delete_user(user, uid): print(f"用户 {uid} 已删除")
delete_user({"name": "Tom", "is_admin": True}, 123)
|
3️⃣ 缓存装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| def cache(func): memo = {} @wraps(func) def wrapper(x): if x not in memo: memo[x] = func(x) return memo[x] return wrapper
@cache def fib(n): if n < 2: return n return fib(n-1) + fib(n-2)
print(fib(35))
|
Python 内置装饰器
| 装饰器 |
用途 |
@staticmethod |
定义静态方法 |
@classmethod |
定义类方法 |
@property |
定义属性方法 |
@functools.lru_cache |
自动缓存函数结果 |
@functools.wraps |
保留原函数元信息 |
示例:
1 2 3 4 5
| from functools import lru_cache
@lru_cache(maxsize=128) def slow_func(x): ...
|
执行时机
1 2 3 4 5 6 7 8 9
| def deco(func): print("装饰器被执行") return func
@deco def hello(): print("函数被调用")
hello()
|
输出:
注意:装饰器在函数定义时执行,而非调用时。
思维导图
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
| 装饰器(Decorator) │ ├── 定义: │ 接受函数 → 返回函数 │ ├── 类型: │ ├── 无参数装饰器 │ ├── 带参数装饰器 │ └── 类装饰器 │ ├── 实现方式: │ ├── 嵌套函数 │ ├── functools.wraps │ ├── 装饰器工厂 │ └── 多装饰器叠加 │ ├── 应用: │ ├── 日志 │ ├── 性能分析 │ ├── 缓存 │ ├── 权限控制 │ └── 异常捕获 │ └── 注意事项: ├── wrap 原函数信息 ├── 注意返回值 ├── 控制装饰器执行时机 └── 不滥用嵌套
|