《Python 装饰器入门:3 个案例让你彻底理解》
Python 装饰器入门:3 个案例让你彻底理解
Python 装饰器是一种强大的语法特性,允许你修改函数或类的行为而不改变其源代码。它基于高阶函数的概念:装饰器本身是一个函数,它接受另一个函数作为输入,并返回一个新函数。这种机制常用于添加日志、缓存、权限检查等功能。本指南将通过三个逐步深入的案例,帮助你彻底理解装饰器的原理和应用。所有代码都使用 Python 语法,并确保可运行。
核心概念:装饰器的工作原理
在 Python 中,装饰器使用 @decorator_name 语法糖来应用。本质上,它等价于: $$ \text{new_func} = \text{decorator}(\text{original_func}) $$ 其中,decorator 是一个函数,它接收 original_func 作为参数,并返回一个包装后的函数 new_func。当调用 original_func 时,实际执行的是 new_func 的逻辑。这利用了闭包特性:新函数可以访问外部作用域的变量。理解这一点是掌握装饰器的关键。
下面,我们通过三个案例逐步构建你的理解。每个案例都包括代码实现、解释和运行示例。
案例 1:简单装饰器——函数执行计时器
这个案例展示一个基础装饰器,用于测量任何函数的执行时间。它不改变原函数的功能,只是添加计时逻辑。
import time
def timer_decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time() # 记录开始时间
result = func(*args, **kwargs) # 执行原函数
end_time = time.time() # 记录结束时间
print(f"函数 {func.__name__} 执行耗时: {end_time - start_time:.6f} 秒")
return result
return wrapper
# 使用装饰器
@timer_decorator
def calculate_sum(n):
"""计算 1 到 n 的和"""
return sum(range(1, n+1))
# 测试
print(calculate_sum(100000)) # 输出: 5000050000 和计时信息
解释和运行结果:
- 工作原理:
timer_decorator是一个装饰器函数。它定义了一个内部函数wrapper,该函数在调用原函数calculate_sum前后添加计时逻辑。*args和**kwargs确保它能处理任意参数。 - 运行示例:当你调用
calculate_sum(100000)时,输出类似:函数 calculate_sum 执行耗时: 0.005123 秒 5000050000 - 关键点:装饰器通过闭包捕获
func变量,确保每次调用都正确计时。这展示了装饰器的非侵入性:无需修改calculate_sum的代码。
案例 2:带参数的装饰器——智能缓存系统
这个案例扩展了装饰器功能,允许传递参数。我们实现一个缓存装饰器,根据参数缓存函数结果,避免重复计算。
def cache_decorator(max_size=3):
"""带参数的装饰器,max_size 控制缓存大小"""
cache = {} # 缓存字典
def decorator(func):
def wrapper(*args):
if args in cache: # 检查缓存
print(f"缓存命中: {func.__name__}{args} -> {cache[args]}")
return cache[args]
result = func(*args) # 执行原函数
if len(cache) >= max_size: # 缓存满时移除最早条目
oldest_key = next(iter(cache))
del cache[oldest_key]
cache[args] = result # 更新缓存
print(f"计算并缓存: {func.__name__}{args} -> {result}")
return result
return wrapper
return decorator
# 使用带参数的装饰器
@cache_decorator(max_size=2) # 设置缓存大小为 2
def fibonacci(n):
"""计算斐波那契数列第 n 项"""
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 测试
print(fibonacci(5)) # 输出: 5 (会多次调用,但缓存优化)
解释和运行结果:
- 工作原理:
cache_decorator是一个工厂函数,它返回真正的装饰器decorator。max_size参数控制缓存大小。内部使用cache字典存储结果,wrapper函数处理缓存逻辑。 - 运行示例:调用
fibonacci(5)时,输出类似:计算并缓存: fibonacci(1) -> 1 计算并缓存: fibonacci(0) -> 0 计算并缓存: fibonacci(2) -> 1 缓存命中: fibonacci(1) -> 1 计算并缓存: fibonacci(3) -> 2 缓存命中: fibonacci(2) -> 1 计算并缓存: fibonacci(4) -> 3 缓存命中: fibonacci(3) -> 2 计算并缓存: fibonacci(5) -> 5 5 - 关键点:带参数的装饰器通过嵌套函数实现。这增强了灵活性,例如
max_size可配置。数学上,这类似于函数组合:$ \text{decorator} = \text{cache_decorator}(\text{max_size}) $,然后应用于 $ \text{fibonacci} $。
案例 3:类装饰器——用户认证系统
这个案例演示装饰器用于类方法,实现权限检查。它检查用户是否有权限执行特定操作,适合 Web 应用或 API。
class AuthDecorator:
"""类装饰器,用于认证用户权限"""
def __init__(self, required_role):
self.required_role = required_role # 所需角色,如 'admin'
def __call__(self, func):
def wrapper(user, *args, **kwargs):
if user.get('role') != self.required_role:
raise PermissionError(f"用户 {user['name']} 无权限执行 {func.__name__}")
return func(user, *args, **kwargs)
return wrapper
# 使用类装饰器
class UserSystem:
@AuthDecorator(required_role='admin') # 装饰器应用于方法
def delete_user(self, user, target_user):
print(f"用户 {target_user} 已被删除")
return True
# 测试
system = UserSystem()
admin_user = {'name': 'Alice', 'role': 'admin'}
normal_user = {'name': 'Bob', 'role': 'user'}
print(system.delete_user(admin_user, 'Bob')) # 输出: 用户 Bob 已被删除
try:
system.delete_user(normal_user, 'Alice') # 抛出异常
except PermissionError as e:
print(e) # 输出: 用户 Bob 无权限执行 delete_user
解释和运行结果:
- 工作原理:
AuthDecorator是一个类,实现了__call__方法,使其行为像函数装饰器。__init__接收参数(如required_role),__call__返回包装函数wrapper,该函数检查用户角色。 - 运行示例:
- 使用
admin_user调用delete_user成功,输出:用户 Bob 已被删除和True。 - 使用
normal_user调用时,抛出PermissionError,输出:用户 Bob 无权限执行 delete_user。
- 使用
- 关键点:类装饰器适用于状态管理(如角色配置)。这体现了装饰器的通用性:它不仅能修饰函数,还能修饰类方法。数学上,装饰器可以视为一个映射:$ \text{原函数} \mapsto \text{增强函数} $。
总结:彻底掌握装饰器
通过以上三个案例,你已经从基础到高级理解了 Python 装饰器:
- 简单装饰器(案例1):添加额外功能(如计时),不改变原函数。
- 带参数装饰器(案例2):通过工厂函数实现可配置行为(如缓存大小)。
- 类装饰器(案例3):用于类方法,支持复杂状态管理(如认证)。
装饰器的优势:
- 代码复用:避免重复代码,例如多个函数共享日志逻辑。
- 模块化:功能增强独立于业务逻辑。
- 灵活性:支持参数化和组合,例如 $ \text{@decorator1} \circ \text{@decorator2} $。
练习建议:尝试自己实现装饰器,如添加日志或重试机制。掌握装饰器后,你将能更高效地编写 Python 代码。如果有疑问,欢迎提供具体场景,我会进一步解析!
更多推荐
所有评论(0)