什么是装饰器?
装饰器(Decorator)是Python中一种强大的语法特性,它允许你在不修改原有函数代码的情况下,动态地改变函数的行为。装饰器本质上是一个高阶函数,它接收一个函数作为参数,并返回一个新的函数。
装饰器的核心概念
装饰器基于Python的几个重要特性:
- 函数是一等公民:函数可以作为参数传递,也可以作为返回值
- 闭包:内部函数可以访问外部函数的变量
- 语法糖:
@decorator语法提供了一种简洁的装饰器应用方式
基础装饰器实现
最简单的装饰器示例
def my_decorator(func):
def wrapper():
print("在函数调用前执行")
func()
print("在函数调用后执行")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
# 调用装饰后的函数
say_hello()
输出结果:
在函数调用前执行
Hello!
在函数调用后执行
带参数的函数装饰器
当被装饰的函数需要参数时,我们需要使用*args和**kwargs:
def parameter_decorator(func):
def wrapper(*args, **kwargs):
print(f"调用函数: {func.__name__}")
print(f"位置参数: {args}")
print(f"关键字参数: {kwargs}")
result = func(*args, **kwargs)
print(f"函数返回: {result}")
return result
return wrapper
@parameter_decorator
def add(a, b):
return a + b
# 测试
result = add(3, 5, x=10)
print(f"最终结果: {result}")
输出:
调用函数: add
位置参数: (3, 5)
关键字参数: {'x': 10}
函数返回: 8
最终结果: 8
装饰器的实际应用场景
1. 计时器装饰器
import time
from functools import wraps
def timer(func):
@wraps(func) # 保留原始函数的元信息
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 执行耗时: {end - start:.4f}秒")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
return "完成"
result = slow_function()
print(result)
2. 缓存/记忆化装饰器
def cache(func):
cached_results = {}
@wraps(func)
def wrapper(*args):
if args in cached_results:
print(f"从缓存中获取结果: {args}")
return cached_results[args]
print(f"计算新结果: {args}")
result = func(*args)
cached_results[args] = result
return result
return wrapper
@cache
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(f"Fibonacci(10) = {fibonacci(10)}")
print(f"Fibonacci(8) = {fibonacci(8)}") # 这次会从缓存获取
3. 权限验证装饰器
def require_permission(permission_level):
def decorator(func):
@wraps(func)
def wrapper(user, *args, **kwargs):
if user.get('permission') < permission_level:
raise PermissionError("权限不足")
return func(user, *args, **kwargs)
return wrapper
return decorator
# 使用
@require_permission(3)
def delete_database(user):
print(f"用户 {user['name']} 正在删除数据库...")
# 测试
admin = {'name': 'Alice', 'permission': 5}
guest = {'name': 'Bob', 'permission': 1}
delete_database(admin) # 成功
try:
delete_database(guest) # 失败
except PermissionError as e:
print(f"错误: {e}")
类装饰器
除了函数装饰器,Python还支持类装饰器:
class CountCalls:
def __init__(self, func):
self.func = func
self.call_count = 0
def __call__(self, *args, **kwargs):
self.call_count += 1
print(f"函数 {self.func.__name__} 被调用了 {self.call_count} 次")
return self.func(*args, **kwargs)
@CountCalls
def example_function():
print("执行主要功能")
example_function()
example_function()
example_function()
输出:
函数 example_function 被调用了 1 次
执行主要功能
函数 example_function 被调用了 2 次
执行主要功能
函数 example_function 被调用了 3 次
执行主要功能
装饰器的高级用法
1. 多个装饰器的组合
def bold(func):
@wraps(func)
def wrapper():
return "<b>" + func() + "</b>"
return wrapper
def italic(func):
@wraps(func)
def wrapper():
return "<i>" + func() + "</i>"
return wrapper
@bold
@italic
def greet():
return "Hello World"
print(greet()) # 输出: <b><i>Hello World</i></b>
2. 带参数的类装饰器
class Repeater:
def __init__(self, times):
self.times = times
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(self.times):
result = func(*args, **kwargs)
return result
return wrapper
@Repeater(times=3)
def say_hi():
print("Hi!")
say_hi()
输出:
Hi!
Hi!
Hi!
装饰器的最佳实践
- 使用functools.wraps:保留原始函数的元信息(名称、文档字符串等)
- 保持装饰器的单一职责:每个装饰器应该只做一件事
- 考虑性能影响:装饰器会增加函数调用的开销
- 编写清晰的文档:说明装饰器的作用和使用方法
- 测试装饰器:确保它们在各种情况下都能正常工作
实战案例:构建一个完整的日志系统
import logging
from datetime import datetime
from functools import wraps
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
def log_execution(level=logging.INFO):
"""记录函数执行日志的装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
logger = logging.getLogger(func.__name__)
# 记录调用信息
logger.log(level, f"开始执行,参数: {args}, {kwargs}")
try:
result = func(*args, **kwargs)
logger.log(level, f"执行成功,返回: {result}")
return result
except Exception as e:
logger.error(f"执行失败: {str(e)}")
raise
return wrapper
return decorator
def validate_input(*validations):
"""输入验证装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for i, (arg, validation) in enumerate(zip(args, validations)):
if not validation(arg):
raise ValueError(f"参数 {i} 验证失败: {arg}")
return func(*args, **kwargs)
return wrapper
return decorator
# 组合使用
@log_execution(logging.INFO)
@validate_input(lambda x: x > 0, lambda y: y > 0)
def divide(a, b):
return a / b
# 测试
try:
print(divide(10, 2))
print(divide(-1, 2)) # 验证失败
except Exception as e:
print(f"错误: {e}")
总结
装饰器是Python中非常强大和优雅的特性,它们可以帮助我们:
- 代码复用:避免重复代码
- 关注点分离:将核心逻辑与横切关注点(如日志、缓存、验证)分离
- 保持代码整洁:使业务逻辑更清晰
掌握装饰器的使用,将使你的Python代码更加Pythonic,更易于维护和扩展。记住,装饰器的核心思想是”包装”,它让我们能够在不修改原有代码的情况下,为函数添加新的功能。
