Python 装饰器的高级用法与实战
引言
Python 装饰器(Decorator)是 Python 中非常强大且优雅的特性之一。简单来说,装饰器是一种用于修改或扩展函数功能的设计模式,它允许我们在不改变原函数代码的情况下,为函数添加额外的行为。装饰器在日志记录、性能监控、权限验证、缓存等场景中有着广泛的应用。
本文将深入探讨 Python 装饰器的高级用法,包括带参数装饰器、装饰器堆叠、类装饰器、带状态装饰器以及实际项目中的应用场景。通过丰富的代码示例和实战案例,帮助读者掌握装饰器的核心概念和实际应用技巧。
一、装饰器基础回顾
在深入高级用法之前,让我们先快速回顾一下装饰器的基础知识。装饰器本质上是一个可调用对象(通常是函数),它接受一个函数作为参数,并返回一个新的函数。最基本的装饰器写法如下:
def simple_decorator(func):
def wrapper():
print("Before calling the function")
func()
print("After calling the function")
return wrapper
@simple_decorator
def say_hello():
print("Hello, World!")
say_hello()
输出结果:
Before calling the function
Hello, World!
After calling the function
这个简单的示例展示了装饰器的基本工作原理:在被装饰函数执行前后添加额外的行为。
二、带参数的装饰器
基础装饰器虽然有用,但有时候我们需要创建可以接受参数的装饰器。这需要一个额外的函数嵌套层级:最外层的函数接受装饰器参数,中间层的函数接受被装饰的函数,最内层的函数是实际的包装函数。
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
results = []
for _ in range(times):
result = func(*args, **kwargs)
results.append(result)
return results if times > 1 else results[0]
return wrapper
return decorator
@repeat(3)
def greet(name):
return f"Hello, {name}!"
print(greet("Alice"))
输出结果:
['Hello, Alice!', 'Hello, Alice!', 'Hello, Alice!']
这个装饰器允许我们指定函数重复执行的次数。在实际应用中,我们可以用类似的思路实现重试机制、超时控制等功能。
三、装饰器堆叠
Python 允许我们将多个装饰器堆叠到同一个函数上。装饰器的执行顺序是从下往上,即最上面的装饰器最先包装原函数。
def uppercase(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result.upper() if isinstance(result, str) else result
return wrapper
def exclaim(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return f"{result}!" if isinstance(result, str) else result
return wrapper
@exclaim
@uppercase
def shout(message):
return message
print(shout("hello world"))
输出结果:
HELLO WORLD!
在这个例子中,先执行 uppercase 装饰器将消息转换为大写,然后 exclaim 装饰器在末尾添加感叹号。装饰器堆叠让我们可以组合多个简单的装饰器,构建复杂的功能。
四、类装饰器
除了函数装饰器,Python 还支持类装饰器。类装饰器是一个实现了 __call__ 方法的类,它可以用来装饰函数或类。
class CountCalls:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"Call #{self.count}")
return self.func(*args, **kwargs)
@CountCalls
def calculate(x, y):
return x + y
print(calculate(5, 3))
print(calculate(10, 20))
输出结果:
Call #1
8
Call #2
30
类装饰器的优势在于可以维护状态(如示例中的调用计数),这在函数装饰器中需要使用闭包或非局部变量才能实现。
五、带状态的装饰器
有时候我们需要装饰器在被调用过程中维护状态。有几种方式可以实现这一点:
1. 使用类装饰器(如上例所示)
2. 使用函数属性
def memoize(func):
func.cache = {}
def wrapper(*args, **kwargs):
key = str(args) + str(kwargs)
if key not in func.cache:
func.cache[key] = func(*args, **kwargs)
return func.cache[key]
wrapper.cache = func.cache
return wrapper
@memoize
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(10))
print(f"Cache contains {len(fibonacci.cache)} entries")
输出结果:
55
Cache contains 11 entries
这个示例实现了一个简单的缓存装饰器,用于存储函数调用的结果,避免重复计算。
<六、保留原函数的元信息
使用装饰器时,原函数的元信息(如 __name__、__doc__ 等)会被包装函数覆盖。为了保留这些信息,Python 提供了 functools.wraps 装饰器。
import functools
def log_execution(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"Executing {func.__name__}")
return func(*args, **kwargs)
return wrapper
@log_execution
def important_function():
"""This is a very important function."""
return 42
print(f"Function name: {important_function.__name__}")
print(f"Function doc: {important_function.__doc__}")
输出结果:
Executing important_function
Function name: important_function
Function doc: This is a very important function.
使用 functools.wraps 是编写生产级装饰器的最佳实践,它确保装饰后的函数保留原函数的元信息,这对于调试和文档生成非常重要。
七、实战应用场景
让我们看几个装饰器在实际项目中的应用场景:
1. 性能监控装饰器
import time
import functools
def measure_time(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} executed in {end_time - start_time:.4f} seconds")
return result
return wrapper
@measure_time
def slow_function():
time.sleep(1)
return "Done"
slow_function()
输出结果:
slow_function executed in 1.0002 seconds
2. 权限验证装饰器
def require_permission(permission):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
user_permissions = get_user_permissions() # 假设的函数
if permission not in user_permissions:
raise PermissionError(f"Permission '{permission}' required")
return func(*args, **kwargs)
return wrapper
return decorator
def get_user_permissions():
return ['read', 'write']
@require_permission('delete')
def delete_file(filename):
print(f"Deleting {filename}")
return True
try:
delete_file("important.txt")
except PermissionError as e:
print(f"Error: {e}")
输出结果:
Error: Permission 'delete' required
3. 重试机制装饰器
def retry(max_attempts, delay=1):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
attempts = 0
while attempts < max_attempts:
try:
return func(*args, **kwargs)
except Exception as e:
attempts += 1
if attempts == max_attempts:
raise
time.sleep(delay)
return None
return wrapper
return decorator
@retry(max_attempts=3, delay=0.5)
def unstable_operation():
import random
if random.random() < 0.7:
raise ConnectionError("Connection failed")
return "Success"
print(unstable_operation())
八、总结
Python 装饰器是一个非常强大的工具,它提供了一种优雅的方式来修改或扩展函数的功能。通过本文的学习,我们了解了:
1. 带参数装饰器的实现方式
2. 如何堆叠多个装饰器
3. 类装饰器的使用场景
4. 如何在装饰器中维护状态
5. 保留原函数元信息的重要性
6. 实际项目中的应用场景(性能监控、权限验证、重试机制等)
装饰器的核心思想是将横切关注点(如日志、缓存、权限检查)从业务逻辑中分离出来,提高代码的可维护性和复用性。掌握装饰器的高级用法,将帮助您编写更加优雅和高效的 Python 代码。
在实际开发中,合理使用装饰器可以显著提升代码质量,让您的程序更加模块化和易于维护。建议读者在自己的项目中尝试使用装饰器,体会其强大的功能。
