Python装饰器从入门到实战
Python装饰器是Python中一个强大且优雅的特性,它允许我们在不修改原函数代码的情况下,动态地为函数添加额外的功能。本文将从基础概念出发,逐步深入到装饰器的实际应用场景,帮助你全面掌握这一重要技术。
什么是装饰器?
装饰器本质上是一个Python函数,它接受一个函数作为参数,并返回一个新的函数。这个新函数通常会在原函数执行前后添加一些额外的逻辑。装饰器使用@符号语法糖来应用,使代码更加简洁优雅。
从数学角度看,装饰器就像是函数的包装器,它不会改变函数的核心逻辑,而是增强或扩展其功能。这种设计模式在AOP(面向切面编程)中非常常见。
装饰器的基本语法
让我们通过一个简单的例子来理解装饰器的基本用法。假设我们想要在函数执行前后打印一些日志信息:
def log_decorator(func):
def wrapper(*args, **kwargs):
print(f"[开始] 执行函数: {func.__name__}")
result = func(*args, **kwargs)
print(f"[结束] 函数 {func.__name__} 执行完成,返回值: {result}")
return result
return wrapper
@log_decorator
def add_numbers(a, b):
"""计算两个数的和"""
return a b
@log_decorator
def multiply_numbers(a, b):
"""计算两个数的积"""
return a * b
# 测试装饰器
add_numbers(5, 3)
print("---分割线---")
multiply_numbers(4, 6)
运行这段代码,你会看到每次调用被装饰的函数时,都会自动打印开始和结束的日志信息,而不需要修改原函数的代码。
带参数的装饰器
有时候我们需要让装饰器接受参数,这样可以更加灵活地控制装饰器的行为。带参数的装饰器实际上是一个装饰器工厂,它返回真正的装饰器:
def repeat_decorator(times):
def decorator(func):
def wrapper(*args, **kwargs):
results = []
for _ in range(times):
result = func(*args, **kwargs)
results.append(result)
return results
return wrapper
return decorator
@repeat_decorator(3)
def greet(name):
"""问候函数"""
return f"你好, {name}!"
# 测试带参数的装饰器
print(greet("小豆包"))
这个装饰器让函数执行指定次数,并收集所有结果返回。你可以轻松修改times参数来改变执行次数。
保留原函数的元数据
使用装饰器后,新函数的__name__、__doc__等属性会被wrapper函数覆盖。为了解决这个问题,Python提供了functools.wraps装饰器:
import functools
def smart_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
"""包装器函数"""
print(f"正在执行: {func.__name__}")
return func(*args, **kwargs)
return wrapper
@smart_decorator
def my_function():
"""我的原始函数"""
return "Hello, World!"
print(f"函数名: {my_function.__name__}")
print(f"文档字符串: {my_function.__doc__}")
使用@functools.wraps后,新函数会保留原函数的所有元数据,这对于调试和文档生成非常重要。
类装饰器
除了函数装饰器,Python还支持类装饰器。类装饰器通过实现__call__方法来使类的实例可以像函数一样调用:
class CountCalls:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count = 1
print(f"函数 {self.func.__name__} 已调用 {self.count} 次")
return self.func(*args, **kwargs)
def get_call_count(self):
return self.count
@CountCalls
def factorial(n):
"""计算阶乘"""
if n <= 1:
return 1
return n * factorial(n - 1)
# 测试类装饰器
print(factorial(5))
print(factorial(3))
print(f"总调用次数: {factorial.get_call_count()}")
类装饰器的优势在于可以维护状态,比如上面例子中的调用计数器。
装饰器的实际应用场景
1. 性能计时装饰器
import time
def timer_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
elapsed_time = end_time - start_time
print(f"{func.__name__} 执行耗时: {elapsed_time:.4f} 秒")
return result
return wrapper
@timer_decorator
def slow_function():
"""模拟耗时操作"""
time.sleep(2)
return "完成"
slow_function()
2. 缓存装饰器
def cache_decorator(func):
cache = {}
@functools.wraps(func)
def wrapper(*args):
if args in cache:
print(f"从缓存获取: {args}")
return cache[args]
result = func(*args)
cache[args] = result
return result
def clear_cache():
cache.clear()
wrapper.clear_cache = clear_cache
return wrapper
@cache_decorator
def fibonacci(n):
"""斐波那契数列(带缓存)"""
if n <= 1:
return n
return fibonacci(n - 1) fibonacci(n - 2)
print(f"fibonacci(10) = {fibonacci(10)}")
print(f"fibonacci(10) = {fibonacci(10)}") # 这次会从缓存获取
3. 权限验证装饰器
def permission_decorator(allowed_roles):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
current_user = get_current_user()
if current_user[role] not in allowed_roles:
raise PermissionError("权限不足")
return func(*args, **kwargs)
return wrapper
return decorator
# 模拟用户系统
def get_current_user():
return {name: test_user, role: guest}
@permission_decorator([admin, editor])
def admin_operation():
return "管理员操作成功"
try:
admin_operation()
except PermissionError as e:
print(f"错误: {e}")
装饰器的叠加使用
Python允许将多个装饰器叠加使用,装饰器会从下往上依次执行:
@timer_decorator
@repeat_decorator(2)
def complex_function(x):
"""复杂函数"""
time.sleep(0.5)
return x ** 2
print(complex_function(3))
在这个例子中,complex_function首先被repeat_decorator装饰,然后被timer_decorator装饰。执行时会先计时,然后重复执行原函数。
装饰器的最佳实践
1. 总是使用functools.wraps
确保装饰器不会破坏原函数的元数据,这对于调试和反射操作至关重要。
2. 保持装饰器简单
装饰器应该专注于单一职责,如果一个装饰器做太多事情,考虑将其拆分成多个装饰器。
3. 处理异常
装饰器应该妥善处理异常,或者在异常发生时提供有意义的错误信息:
def safe_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"函数 {func.__name__} 执行出错: {e}")
raise
return wrapper
4. 文档化装饰器
为装饰器添加清晰的文档说明,包括其用途、参数和示例。
总结
Python装饰器是一个强大而优雅的特性,它可以帮助我们编写更加简洁、可维护的代码。通过装饰器,我们可以:
- 在不修改原函数代码的情况下添加额外功能
- 实现横切关注点的分离(如日志、计时、缓存等)
- 提高代码的可重用性和可维护性
从简单的日志记录到复杂的权限验证,装饰器在Python开发中有着广泛的应用场景。掌握装饰器将让你的Python代码更加Pythonic,更加优雅。
建议在实际项目中多使用装饰器,逐步体会它的魅力。当你发现一段代码在多个函数中重复出现时,考虑用装饰器来重构它,你会发现代码会变得更加清晰和易于维护。
继续探索Python装饰器的更多可能性,你会发现它远比你想象的更加强大!
