什么是装饰器及其核心概念
装饰器(Decorator)是Python中一种强大的语法特性,它允许在不修改原有函数代码的情况下,动态地扩展或修改函数的行为。装饰器本质上是一个高阶函数,它接收一个函数作为参数,并返回一个新的函数。
装饰器的核心价值
- 代码复用:将通用功能(如日志记录、性能测试、权限验证)封装成装饰器,可以在多个函数中重复使用
- 关注点分离:将核心业务逻辑与辅助功能(如日志、缓存)分离,提高代码可读性
- 非侵入式扩展:无需修改原函数代码即可添加新功能
基本装饰器示例
def logger(func):
"""一个简单的日志装饰器"""
def wrapper(*args, **kwargs):
print(f"开始执行函数: {func.__name__}")
result = func(*args, **kwargs)
print(f"函数执行完成: {func.__name__}")
return result
return wrapper
@logger
def add(a, b):
return a + b
# 调用示例
print(add(3, 5))
输出结果:
开始执行函数: add
函数执行完成: add
8
装饰器的进阶用法
带参数的装饰器
有时我们需要为装饰器本身传递参数,这时需要创建三层嵌套函数:
def repeat(num_times):
"""重复执行函数的装饰器"""
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator_repeat
@repeat(num_times=3)
def greet(name):
print(f"Hello, {name}!")
# 调用示例
greet("Alice")
输出结果:
Hello, Alice!
Hello, Alice!
Hello, Alice!
类装饰器
Python还支持使用类作为装饰器,这通常在需要维护状态时很有用:
class CountCalls:
"""统计函数调用次数的类装饰器"""
def __init__(self, func):
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print(f"调用次数: {self.num_calls}")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
# 调用示例
say_hello()
say_hello()
输出结果:
调用次数: 1
Hello!
调用次数: 2
Hello!
实际应用场景
1. 性能监控装饰器
import time
import functools
def timer(func):
"""测量函数执行时间的装饰器"""
@functools.wraps(func) # 保留原函数的元信息
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
print(f"{func.__name__} 执行时间: {end_time - start_time:.4f}秒")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
# 调用示例
slow_function()
2. 缓存装饰器(记忆化)
def memoize(func):
"""缓存函数结果的装饰器"""
cache = {}
@functools.wraps(func)
def wrapper(*args):
if args in cache:
return cache[args]
result = func(*args)
cache[args] = result
return result
return wrapper
@memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 调用示例(会显著提高递归效率)
print(fibonacci(30)) # 快速计算
3. 权限验证装饰器
def requires_role(role):
"""基于角色的访问控制装饰器"""
def decorator(func):
@functools.wraps(func)
def wrapper(user, *args, **kwargs):
if user.get('role') != role:
raise PermissionError(f"需要{role}权限")
return func(user, *args, **kwargs)
return wrapper
return decorator
@requires_role('admin')
def delete_user(user):
print(f"用户{user['name']}已被删除")
# 调用示例
admin = {'name': 'Alice', 'role': 'admin'}
user = {'name': 'Bob', 'role': 'user'}
delete_user(admin) # 成功执行
# delete_user(user) # 会抛出PermissionError
装饰器的注意事项
1. 使用functools.wraps
在编写装饰器时,应该始终使用@functools.wraps装饰内部包装函数,这样可以保留原函数的元信息(如函数名、文档字符串等):
import functools
def bad_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper # 不使用wraps
def good_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper # 使用wraps
@bad_decorator
def func1():
"""原始文档字符串"""
pass
@good_decorator
def func2():
"""原始文档字符串"""
pass
print(func1.__name__) # 输出: wrapper
print(func1.__doc__) # 输出: None
print(func2.__name__) # 输出: func2
print(func2.__doc__) # 输出: 原始文档字符串
2. 装饰器执行顺序
当多个装饰器堆叠时,它们的执行顺序是从下往上的:
def decorator1(func):
print("装饰器1 - 装饰")
def wrapper(*args, **kwargs):
print("装饰器1 - 执行前")
result = func(*args, **kwargs)
print("装饰器1 - 执行后")
return result
return wrapper
def decorator2(func):
print("装饰器2 - 装饰")
def wrapper(*args, **kwargs):
print("装饰器2 - 执行前")
result = func(*args, **kwargs)
print("装饰器2 - 执行后")
return result
return wrapper
@decorator1
@decorator2
def my_func():
print("原函数执行")
# 调用
my_func()
输出顺序:
装饰器2 - 装饰
装饰器1 - 装饰
装饰器1 - 执行前
装饰器2 - 执行前
原函数执行
装饰器2 - 执行后
装饰器1 - 执行后
高级技巧:类方法装饰器
1. 实例方法装饰器
def method_decorator(func):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
print(f"调用方法: {func.__name__}")
return func(self, *args, **kwargs)
return wrapper
class Calculator:
@method_decorator
def add(self, a, b):
return a + b
calc = Calculator()
print(calc.add(2, 3))
2. 静态方法和类方法装饰器
class MyClass:
@staticmethod
@staticmethod_decorator # 注意顺序:装饰器从下往上应用
def static_method():
return "静态方法"
@classmethod
@classmethod_decorator
def class_method(cls):
return f"类方法: {cls.__name__}"
装饰器库推荐
Python社区有许多现成的装饰器库可以节省开发时间:
- functools:Python标准库,提供lru_cache等实用装饰器
- wrapt:功能强大的装饰器库,处理复杂场景
- decorator:简化装饰器创建的库
- click:命令行工具库,提供丰富的装饰器
functools.lru_cache示例
from functools import lru_cache
@lru_cache(maxsize=128)
def expensive_calculation(n):
print(f"计算{n}")
return n * n
# 第一次调用会执行计算
print(expensive_calculation(4)) # 输出: 计算4 → 16
# 第二次调用直接返回缓存结果
print(expensive_calculation(4)) # 输出: 16
总结
装饰器是Python中非常强大且优雅的特性,掌握它们可以:
- 大幅减少重复代码
- 提高代码的可维护性
- 实现更清晰的关注点分离
- 构建更灵活的架构
关键要点:
- 始终使用
functools.wraps保留原函数元信息 - 理解装饰器的执行顺序
- 根据场景选择合适的装饰器类型(函数/类)
- 考虑使用现成的装饰器库
通过合理使用装饰器,你可以让Python代码更加简洁、高效和专业。
