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"))
def call_twice(func, arg): return func(arg) + " " + func(arg)
result = call_twice(greet, "Bob") print(result)
def make_multiplier(factor): def multiplier(number): return number * factor return multiplier
double = make_multiplier(2) print(double(5))
|
闭包的核心机制
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))
print(closure.__closure__) print(closure.__closure__[0].cell_contents)
|
装饰器的基础实现
最简单的装饰器
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: 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(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 "执行业务逻辑"
|
装饰器的最佳实践
- 保持函数元数据:始终使用
functools.wraps
- 处理异常:确保装饰器不会掩盖原函数的异常
- 性能考虑:避免在装饰器中进行昂贵的操作
- 可配置性:使用装饰器工厂提供灵活的配置选项
- 文档化:为装饰器提供清晰的文档说明
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 优雅哲学的完美体现。