装饰器

装饰器的本质是一个函数,它接收另一个函数作为参数(被装饰函数),返回一个新的函数对象。

简单说:

“装饰器 = 一个接收函数并返回函数的函数。”

例子:

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
3
函数开始执行
Hello!
函数执行结束

等价于:

1
2
say_hello = decorator(say_hello)
say_hello()
  • @decorator 只是语法糖。
  • 装饰器的返回值会替换原函数。
  • 本质是:func = decorator(func)

工作机制

1
2
3
@装饰器名
def 函数名(...):
...

等价于:

1
函数名 = 装饰器名(函数名)

执行顺序:

  1. Python 解释器加载函数定义;
  2. 看到 @装饰器 标记;
  3. 立即调用装饰器,将函数传入;
  4. 把装饰器返回的新函数对象重新绑定到原函数名上。

为什么需要装饰器?

装饰器常用于在函数执行前后插入通用逻辑,比如:

场景 功能
日志记录 打印函数调用信息
性能分析 统计函数执行时间
权限控制 检查用户是否有权限
缓存机制 保存上次执行结果
异常处理 自动捕获并记录错误

这样可以让逻辑“可插拔”,而无需修改业务代码。

装饰器类型

带参函数的装饰器

上面的例子只适用于无参函数。如果被装饰函数有参数,装饰器应使用 *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

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__) # wrapper
print(func.__doc__) # Wrapper docstring

这会破坏调试与文档生成

✅ 解决办法:

使用 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
装饰器被执行
函数被调用

注意:装饰器在函数定义时执行,而非调用时。

思维导图

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 原函数信息
├── 注意返回值
├── 控制装饰器执行时机
└── 不滥用嵌套