Python 装饰器深度解析:从基础语法到高级应用的完整指南

Python 装饰器深度解析:从基础语法到高级应用的完整指南

引言

Python 装饰器是一个强大而优雅的语言特性,它允许我们在不修改原函数代码的情况下,动态地扩展或修改函数的行为。从简单的日志记录到复杂的权限控制,装饰器在现代 Python 开发中扮演着重要角色。本文将深入探讨装饰器的核心原理、实现机制和高级应用,帮助开发者全面掌握这一关键技术。

装饰器的本质:函数是一等公民

理解闭包和高阶函数

在深入装饰器之前,我们需要理解 Python 中函数的特殊性质:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 函数作为对象
def greet(name):
return f"Hello, {name}!"

# 函数可以赋值给变量
my_func = greet
print(my_func("Alice")) # 输出: Hello, Alice!

# 函数可以作为参数传递
def call_twice(func, arg):
return func(arg) + " " + func(arg)

result = call_twice(greet, "Bob")
print(result) # 输出: Hello, Bob! Hello, Bob!

# 函数可以作为返回值
def make_multiplier(factor):
def multiplier(number):
return number * factor
return multiplier

double = make_multiplier(2)
print(double(5)) # 输出: 10

闭包的核心机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def outer_function(x):
# 外部函数的局部变量
outer_var = x

def inner_function(y):
# 内部函数可以访问外部函数的变量
return outer_var + y

# 返回内部函数(闭包)
return inner_function

# 创建闭包
closure = outer_function(10)
print(closure(5)) # 输出: 15

# 查看闭包的自由变量
print(closure.__closure__) # 显示闭包捕获的变量
print(closure.__closure__[0].cell_contents) # 输出: 10

装饰器的基础实现

最简单的装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def my_decorator(func):
def wrapper():
print("装饰器执行前")
result = func()
print("装饰器执行后")
return result
return wrapper

# 手动应用装饰器
def say_hello():
print("Hello, World!")

decorated_func = my_decorator(say_hello)
decorated_func()

# 使用 @ 语法糖
@my_decorator
def say_goodbye():
print("Goodbye, World!")

say_goodbye()

处理函数参数的装饰器

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
import functools
from typing import Any, Callable

def timing_decorator(func: Callable) -> Callable:
"""计算函数执行时间的装饰器"""
@functools.wraps(func) # 保持原函数的元数据
def wrapper(*args: Any, **kwargs: Any) -> Any:
import time
start_time = time.time()

try:
result = func(*args, **kwargs)
return result
finally:
end_time = time.time()
execution_time = end_time - start_time
print(f"{func.__name__} 执行时间: {execution_time:.4f} 秒")

return wrapper

@timing_decorator
def slow_function(n: int) -> int:
"""一个耗时的函数"""
import time
time.sleep(1)
return sum(range(n))

result = slow_function(1000)
print(f"结果: {result}")

带参数的装饰器

装饰器工厂模式

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
def retry(max_attempts: int = 3, delay: float = 1.0):
"""重试装饰器工厂"""
def decorator(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
import time
import random

last_exception = None

for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
last_exception = e
if attempt < max_attempts - 1:
print(f"第 {attempt + 1} 次尝试失败: {e}")
time.sleep(delay)
else:
print(f"所有 {max_attempts} 次尝试都失败了")

# 重新抛出最后一个异常
raise last_exception

return wrapper
return decorator

@retry(max_attempts=3, delay=0.5)
def unreliable_network_call():
"""模拟不稳定的网络调用"""
import random
if random.random() < 0.7: # 70% 的概率失败
raise ConnectionError("网络连接失败")
return "请求成功"

# 测试重试机制
try:
result = unreliable_network_call()
print(result)
except ConnectionError as e:
print(f"最终失败: {e}")

缓存装饰器的实现

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
def memoize(maxsize: int = 128):
"""LRU 缓存装饰器"""
def decorator(func: Callable) -> Callable:
cache = {}
access_order = []

@functools.wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
# 创建缓存键
key = str(args) + str(sorted(kwargs.items()))

# 检查缓存
if key in cache:
# 更新访问顺序
access_order.remove(key)
access_order.append(key)
return cache[key]

# 计算结果
result = func(*args, **kwargs)

# 缓存管理
if len(cache) >= maxsize:
# 移除最久未使用的项
oldest_key = access_order.pop(0)
del cache[oldest_key]

# 添加到缓存
cache[key] = result
access_order.append(key)

return result

# 添加缓存统计方法
wrapper.cache_info = lambda: {
'size': len(cache),
'maxsize': maxsize,
'keys': list(cache.keys())
}
wrapper.cache_clear = lambda: cache.clear() or access_order.clear()

return wrapper
return decorator

@memoize(maxsize=3)
def fibonacci(n: int) -> int:
"""计算斐波那契数列"""
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)

# 测试缓存效果
print(fibonacci(10)) # 第一次计算
print(fibonacci(10)) # 从缓存获取
print(fibonacci.cache_info()) # 查看缓存状态

类装饰器和方法装饰器

类装饰器的实现

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
class CountCalls:
"""统计函数调用次数的类装饰器"""

def __init__(self, func: Callable):
self.func = func
self.count = 0
functools.update_wrapper(self, func)

def __call__(self, *args: Any, **kwargs: Any) -> Any:
self.count += 1
print(f"{self.func.__name__} 被调用了 {self.count} 次")
return self.func(*args, **kwargs)

def reset_count(self) -> None:
"""重置计数器"""
self.count = 0

@CountCalls
def greet(name: str) -> str:
return f"Hello, {name}!"

print(greet("Alice"))
print(greet("Bob"))
print(f"总调用次数: {greet.count}")
greet.reset_count()

方法装饰器和属性装饰器

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
class ValidationError(Exception):
pass

def validate_positive(func: Callable) -> Callable:
"""验证参数为正数的装饰器"""
@functools.wraps(func)
def wrapper(self, value: float) -> Any:
if value <= 0:
raise ValidationError(f"值必须为正数,得到: {value}")
return func(self, value)
return wrapper

class BankAccount:
def __init__(self, initial_balance: float = 0):
self._balance = initial_balance

@property
def balance(self) -> float:
"""获取账户余额"""
return self._balance

@validate_positive
def deposit(self, amount: float) -> None:
"""存款"""
self._balance += amount
print(f"存款 {amount},当前余额: {self._balance}")

@validate_positive
def withdraw(self, amount: float) -> None:
"""取款"""
if amount > self._balance:
raise ValidationError("余额不足")
self._balance -= amount
print(f"取款 {amount},当前余额: {self._balance}")

# 测试方法装饰器
account = BankAccount(100)
account.deposit(50)
try:
account.withdraw(-10) # 触发验证错误
except ValidationError as e:
print(f"验证失败: {e}")

高级装饰器模式

装饰器链和组合

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
def log_calls(func: Callable) -> Callable:
"""记录函数调用的装饰器"""
@functools.wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
print(f"调用 {func.__name__} 参数: args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} 返回: {result}")
return result
return wrapper

def validate_types(**expected_types):
"""类型验证装饰器"""
def decorator(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
# 获取函数签名
import inspect
sig = inspect.signature(func)
bound_args = sig.bind(*args, **kwargs)
bound_args.apply_defaults()

# 验证类型
for param_name, expected_type in expected_types.items():
if param_name in bound_args.arguments:
value = bound_args.arguments[param_name]
if not isinstance(value, expected_type):
raise TypeError(
f"参数 {param_name} 期望类型 {expected_type.__name__}, "
f"实际类型 {type(value).__name__}"
)

return func(*args, **kwargs)
return wrapper
return decorator

# 装饰器链的应用
@log_calls
@timing_decorator
@validate_types(x=int, y=int)
def calculate_power(x: int, y: int) -> int:
"""计算 x 的 y 次方"""
return x ** y

# 测试装饰器链
result = calculate_power(2, 3)
print(f"最终结果: {result}")

上下文管理装饰器

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
62
63
64
65
66
67
68
from contextlib import contextmanager
import threading

class DatabaseConnection:
"""模拟数据库连接"""

def __init__(self):
self.connected = False
self.transaction_active = False

def connect(self):
print("连接数据库")
self.connected = True

def disconnect(self):
print("断开数据库连接")
self.connected = False

def begin_transaction(self):
print("开始事务")
self.transaction_active = True

def commit(self):
print("提交事务")
self.transaction_active = False

def rollback(self):
print("回滚事务")
self.transaction_active = False

def database_transaction(db_connection: DatabaseConnection):
"""数据库事务装饰器"""
def decorator(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
if not db_connection.connected:
db_connection.connect()

db_connection.begin_transaction()
try:
result = func(*args, **kwargs)
db_connection.commit()
return result
except Exception as e:
db_connection.rollback()
print(f"事务回滚,原因: {e}")
raise
return wrapper
return decorator

# 使用数据库事务装饰器
db = DatabaseConnection()

@database_transaction(db)
def update_user_data(user_id: int, data: dict):
"""更新用户数据"""
print(f"更新用户 {user_id} 的数据: {data}")
# 模拟可能的异常
if data.get('invalid'):
raise ValueError("无效的数据")
return "更新成功"

# 测试事务处理
try:
update_user_data(123, {'name': 'Alice'})
update_user_data(456, {'invalid': True}) # 触发异常
except ValueError:
print("处理了数据验证错误")

装饰器的性能考量和最佳实践

性能优化技巧

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
import time
from functools import lru_cache, wraps

# 使用内置的 lru_cache 而不是自定义缓存
@lru_cache(maxsize=128)
def expensive_calculation(n: int) -> int:
"""使用内置缓存的昂贵计算"""
time.sleep(0.1) # 模拟耗时操作
return sum(range(n))

# 避免在装饰器中进行昂贵操作
def efficient_decorator(func: Callable) -> Callable:
"""高效的装饰器实现"""
# 在装饰时进行一次性的准备工作
func_name = func.__name__

@wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
# 运行时的开销最小化
return func(*args, **kwargs)

return wrapper

# 装饰器的条件应用
def conditional_decorator(condition: bool):
"""条件装饰器"""
def decorator(func: Callable) -> Callable:
if condition:
return timing_decorator(func)
else:
return func # 不应用装饰器
return decorator

# 根据环境变量决定是否启用性能监控
import os
DEBUG_MODE = os.getenv('DEBUG', 'False').lower() == 'true'

@conditional_decorator(DEBUG_MODE)
def production_function():
"""生产环境函数"""
return "执行业务逻辑"

装饰器的最佳实践

  1. 保持函数元数据:始终使用 functools.wraps
  2. 处理异常:确保装饰器不会掩盖原函数的异常
  3. 性能考虑:避免在装饰器中进行昂贵的操作
  4. 可配置性:使用装饰器工厂提供灵活的配置选项
  5. 文档化:为装饰器提供清晰的文档说明
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
def robust_decorator(func: Callable) -> Callable:
"""健壮的装饰器模板

Args:
func: 被装饰的函数

Returns:
装饰后的函数

Example:
@robust_decorator
def my_function():
return "Hello"
"""
@functools.wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
try:
# 前置处理
print(f"执行前: {func.__name__}")

# 调用原函数
result = func(*args, **kwargs)

# 后置处理
print(f"执行后: {func.__name__}")

return result
except Exception as e:
# 异常处理
print(f"函数 {func.__name__} 执行异常: {e}")
raise # 重新抛出异常

return wrapper

总结

Python 装饰器是一个强大的语言特性,它基于函数是一等公民和闭包机制,为我们提供了优雅的代码扩展方式。从简单的函数包装到复杂的框架级功能,装饰器在现代 Python 开发中无处不在。

掌握装饰器的关键在于理解其本质:装饰器就是接受函数作为参数并返回新函数的高阶函数。通过深入理解闭包、函数对象和元编程概念,我们可以创建出既强大又优雅的装饰器解决方案。

在实际应用中,我们应该遵循最佳实践,注重性能和可维护性,合理使用装饰器来提高代码的复用性和可读性。随着对装饰器理解的深入,你会发现它不仅是一个语法特性,更是 Python 优雅哲学的完美体现。