什么是装饰器?
装饰器(Decorator)是Python中一种强大的语法特性,它允许你在不修改原有函数代码的情况下,为函数添加额外的功能。装饰器本质上是一个高阶函数,它接收一个函数作为参数,并返回一个新的函数。
装饰器的基本概念
想象一下,你有一个函数,你想在不改变这个函数本身的情况下,给它添加一些额外的功能,比如日志记录、性能测试、权限验证等。装饰器就是为了解决这个问题而生的。
# 最简单的装饰器示例
def my_decorator(func):
def wrapper():
print("函数执行前")
func()
print("函数执行后")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
# 调用函数
say_hello()
输出:
函数执行前
Hello!
函数执行后
装饰器的工作原理
1. 函数作为一等公民
在Python中,函数是一等公民,这意味着函数可以:
- 被赋值给变量
- 作为参数传递给其他函数
- 作为其他函数的返回值
def greet():
return "Hello!"
# 函数赋值给变量
greeting = greet
print(greeting()) # 输出: Hello!
# 函数作为参数
def call_function(func):
return func()
print(call_function(greet)) # 输出: Hello!
2. 闭包的概念
装饰器利用了Python的闭包特性。闭包是指一个函数可以访问它被定义时的环境(包括局部变量)。
def outer_function(msg):
message = msg
def inner_function():
print(message)
return inner_function
my_func = outer_function("Hello, World!")
my_func() # 输出: Hello, World!
基础装饰器实现
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:.4f} 秒")
return result
return wrapper
@timer_decorator
def slow_function():
"""模拟耗时操作"""
time.sleep(1)
return "完成"
result = slow_function()
print(result)
2. 带参数的装饰器
有时候我们需要让装饰器本身也能接收参数:
def repeat_decorator(times):
"""重复执行函数的装饰器"""
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat_decorator(3)
def greet(name):
print(f"你好, {name}!")
greet("小明")
输出:
你好, 小明!
你好, 小明!
你好, 小明!
高级装饰器技术
1. 使用functools.wraps
当我们使用装饰器时,原函数的元信息(如函数名、文档字符串)会被包装函数覆盖。使用functools.wraps可以保留原函数的元信息:
import functools
def logging_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"调用函数: {func.__name__}")
return func(*args, **kwargs)
return wrapper
@logging_decorator
def example_function():
"""这是一个示例函数"""
pass
print(example_function.__name__) # 输出: example_function
print(example_function.__doc__) # 输出: 这是一个示例函数
2. 类装饰器
除了函数装饰器,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 say_hello():
print("Hello!")
say_hello()
say_hello()
say_hello()
3. 装饰器堆叠(多层装饰器)
Python允许多个装饰器堆叠使用,它们的执行顺序是从下到上:
def bold_decorator(func):
def wrapper():
return f"<b>{func()}</b>"
return wrapper
def italic_decorator(func):
def wrapper():
return f"<i>{func()}</i>"
return wrapper
@bold_decorator
@italic_decorator
def hello():
return "Hello!"
print(hello()) # 输出: <b><i>Hello!</i></b>
实际应用场景
1. 日志记录
import logging
import functools
logging.basicConfig(level=logging.INFO)
def log_execution(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
logging.info(f"开始执行: {func.__name__},参数: {args}, {kwargs}")
try:
result = func(*args, **kwargs)
logging.info(f"函数 {func.__name__} 执行成功")
return result
except Exception as e:
logging.error(f"函数 {func.__name__} 执行失败: {e}")
raise
return wrapper
@log_execution
def divide(a, b):
return a / b
# 测试
try:
print(divide(10, 2))
print(divide(10, 0))
except:
pass
2. 缓存/记忆化
import functools
import time
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)
# 测试性能
start = time.time()
print(fibonacci(30))
print(f"耗时: {time.time() - start:.4f} 秒")
3. 权限验证
def require_admin(func):
"""需要管理员权限的装饰器"""
@functools.wraps(func)
def wrapper(user, *args, **kwargs):
if user.get('role') != 'admin':
raise PermissionError("需要管理员权限")
return func(user, *args, **kwargs)
return wrapper
@require_admin
def delete_user(user, target_user):
print(f"用户 {user['name']} 删除了用户 {target_user}")
# 测试
admin_user = {'name': 'Alice', 'role': 'admin'}
regular_user = {'name': 'Bob', 'role': 'user'}
try:
delete_user(admin_user, 'Charlie') # 成功
delete_user(regular_user, 'Charlie') # 失败
except PermissionError as e:
print(f"错误: {e}")
4. 性能监控
import time
import functools
def performance_monitor(func):
"""监控函数性能的装饰器"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
execution_time = end - start
if execution_time > 1.0:
logging.warning(f"函数 {func.__name__} 执行缓慢: {execution_time:.4f} 秒")
return result
return wrapper
@performance_monitor
def heavy_computation():
time.sleep(1.5)
return "计算完成"
print(heavy_computation())
装饰器的参数和返回值处理
1. 通用装饰器模板
import functools
def universal_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# 前置处理
print(f"准备调用 {func.__name__}")
# 调用原函数
result = func(*args, **kwargs)
# 后置处理
print(f"{func.__name__} 返回结果: {result}")
return result
return wrapper
@universal_decorator
def add(a, b):
return a + b
print(add(3, 5))
2. 处理特殊场景
def validate_input(*validations):
"""参数验证装饰器"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# 验证位置参数
for i, (arg, validation) in enumerate(zip(args, validations)):
if not validation(arg):
raise ValueError(f"参数 {i} 验证失败: {arg}")
# 验证关键字参数
for key, value in kwargs.items():
if key in validations and not validations[key](value):
raise ValueError(f"关键字参数 {key} 验证失败: {value}")
return func(*args, **kwargs)
return wrapper
return decorator
# 使用
@validate_input(lambda x: x > 0, lambda y: y > 0)
def divide_positive(a, b):
return a / b
print(divide_positive(10, 2)) # 成功
# print(divide_positive(-1, 2)) # 会抛出ValueError
装饰器的调试技巧
1. 使用name和doc检查
def debug_decorator(func):
def wrapper(*args, **kwargs):
print(f"调试: 调用 {func.__name__},参数: {args}, {kwargs}")
result = func(*args, **kwargs)
print(f"调试: 返回 {result}")
return result
return wrapper
@debug_decorator
def test_func(x):
"""测试函数文档"""
return x * 2
print(f"函数名: {test_func.__name__}")
print(f"文档: {test_func.__doc__}")
2. 使用inspect模块检查签名
import inspect
def inspect_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
sig = inspect.signature(func)
bound = sig.bind(*args, **kwargs)
bound.apply_defaults()
print(f"调用 {func.__name__},参数: {dict(bound.arguments)}")
return func(*args, **kwargs)
return wrapper
@inspect_decorator
def example(a, b=2, c=3):
return a + b + c
example(1, c=4)
常见陷阱和解决方案
1. 装饰器不保留原函数元信息
问题:
def bad_decorator(func):
def wrapper():
return func()
return wrapper
@bad_decorator
def test():
"""测试文档"""
pass
print(test.__name__) # 输出: wrapper
print(test.__doc__) # 输出: None
解决方案:
import functools
def good_decorator(func):
@functools.wraps(func)
def wrapper():
return func()
return wrapper
@good_decorator
def test():
"""测试文档"""
pass
print(test.__name__) # 输出: test
print(test.__doc__) # 输出: 测试文档
2. 装饰器参数传递错误
# 错误示例
def wrong_decorator(func):
def wrapper(*args, **kwargs):
print("装饰器参数:", func) # 这里会打印函数对象,而不是参数
return func(*args, **kwargs)
return wrapper
@wrong_decorator("参数1", "参数2")
def example():
pass
正确做法:
def correct_decorator(*dec_args, **dec_kwargs):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("装饰器参数:", dec_args, dec_kwargs)
return func(*args, **kwargs)
return wrapper
return decorator
@correct_decorator("参数1", "参数2", key="value")
def example():
pass
example()
装饰器的最佳实践
1. 保持装饰器的单一职责
# 不好的做法 - 一个装饰器做太多事
def messy_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# 日志
print(f"调用 {func.__name__}")
# 权限检查
if not check_permission():
raise PermissionError()
# 性能监控
start = time.time()
result = func(*args, **kwargs)
print(f"耗时: {time.time() - start}")
# 缓存
cache[args] = result
return result
return wrapper
# 好的做法 - 分离关注点
def log_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__}")
return func(*args, **kwargs)
return wrapper
def permission_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if not check_permission():
raise PermissionError()
return func(*args, **kwargs)
return wrapper
def performance_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
print(f"耗时: {time.time() - start}")
return result
return wrapper
# 组合使用
@log_decorator
@permission_decorator
@performance_decorator
def clean_function():
pass
2. 为装饰器添加配置选项
def configurable_decorator(
enable_logging=True,
enable_timing=False,
log_level="INFO"
):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if enable_logging:
print(f"[{log_level}] 调用 {func.__name__}")
if enable_timing:
start = time.time()
result = func(*args, **kwargs)
print(f"耗时: {time.time() - start}")
return result
else:
return func(*args, **kwargs)
return wrapper
return decorator
# 使用
@configurable_decorator(enable_logging=True, enable_timing=True, log_level="DEBUG")
def my_function():
time.sleep(0.1)
return "完成"
my_function()
装饰器的高级应用:元类装饰器
1. 类装饰器的高级用法
class Singleton:
"""单例模式的类装饰器"""
def __init__(self, cls):
self.cls = cls
self.instance = None
def __call__(self, *args, **kwargs):
if self.instance is None:
self.instance = self.cls(*args, **kwargs)
return self.instance
def __getattr__(self, name):
return getattr(self.cls, name)
@Singleton
class DatabaseConnection:
def __init__(self):
print("创建数据库连接...")
self.connected = True
# 测试
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(f"是否是同一个实例: {db1 is db2}") # True
2. 使用装饰器实现策略模式
class Strategy:
"""策略模式装饰器"""
def __init__(self, name):
self.name = name
self.strategies = {}
def register(self, func):
self.strategies[func.__name__] = func
return func
def execute(self, strategy_name, *args, **kwargs):
if strategy_name not in self.strategies:
raise ValueError(f"未知策略: {strategy_name}")
return self.strategies[strategy_name](*args, **kwargs)
# 使用
payment_strategy = Strategy("Payment")
@payment_strategy.register
def credit_card(amount):
return f"信用卡支付: {amount}元"
@payment_strategy.register
def alipay(amount):
return f"支付宝支付: {amount}元"
print(payment_strategy.execute("credit_card", 100))
print(payment_strategy.execute("alipay", 200))
装饰器的性能考虑
1. 装饰器的性能开销
import time
def measure_overhead(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
def raw_function(n):
return sum(range(n))
def decorated_function(n):
return sum(range(n))
# 测试性能差异
n = 1000000
# 原始函数
start = time.time()
raw_function(n)
raw_time = time.time() - start
# 装饰后的函数
decorated = measure_overhead(decorated_function)
start = time.time()
decorated(n)
decorated_time = time.time() - start
print(f"原始函数耗时: {raw_time:.6f}秒")
print(f"装饰后函数耗时: {decorated_time:.6f}秒")
print(f"开销: {(decorated_time - raw_time) / raw_time * 100:.2f}%")
2. 优化装饰器性能
# 使用functools.lru_cache优化递归装饰器
import functools
def expensive_decorator(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
# 更好的方式:直接使用functools.lru_cache
@functools.lru_cache(maxsize=128)
def fibonacci_optimized(n):
if n < 2:
return n
return fibonacci_optimized(n-1) + fibonacci_optimized(n-2)
# 测试
print(fibonacci_optimized(30))
print(f"缓存信息: {fibonacci_optimized.cache_info()}")
装饰器的测试策略
1. 测试装饰器本身
import unittest
from unittest.mock import patch
def test_logging_decorator():
"""测试日志装饰器"""
with patch('builtins.print') as mock_print:
@log_execution
def test_func():
return "result"
result = test_func()
# 验证打印了开始和结束信息
assert mock_print.call_count == 2
assert "开始执行" in mock_print.call_args_list[0][0][0]
assert "执行成功" in mock_print.call_args_list[1][0][0]
assert result == "result"
# 运行测试
test_logging_decorator()
print("装饰器测试通过!")
2. 测试被装饰的函数
def test_decorated_function():
"""测试被装饰的函数行为"""
@memoize
def add(a, b):
return a + b
# 第一次调用
result1 = add(1, 2)
assert result1 == 3
# 第二次调用相同参数,应该从缓存返回
result2 = add(1, 2)
assert result2 == 3
# 不同参数
result3 = add(2, 3)
assert result3 == 5
print("所有测试通过!")
test_decorated_function()
总结
装饰器是Python中非常强大和灵活的特性,掌握它们可以让你的代码更加优雅和可维护。记住以下要点:
- 理解基础:装饰器本质上是高阶函数,利用了闭包特性
- 使用functools.wraps:保留原函数的元信息
- 处理参数:正确处理位置参数和关键字参数
- 组合使用:可以堆叠多个装饰器
- 保持简洁:每个装饰器应该只做一件事
- 考虑性能:注意装饰器的性能开销
- 充分测试:测试装饰器本身和被装饰的函数
通过实践这些概念和技巧,你将能够创建出功能强大、易于维护的装饰器,显著提升你的Python编程技能。
