Python 装饰器模式
装饰器模式的核心思想是包装——通过创建一个包装对象(装饰器)来包裹原始对象,从而在不修改原始对象的前提下扩展其功能。
基本概念
装饰器模式包含四个主要角色:
- 组件接口(Component):定义了被装饰对象和装饰器的共同接口
- 具体组件(Concrete Component):需要被装饰的原始对象
- 装饰器基类(Decorator):持有一个组件对象的引用,并实现组件接口
- 具体装饰器(Concrete Decorator):实现具体的装饰功能

装饰器的基本语法
函数装饰器
函数装饰器是最常见的装饰器形式,它接收一个函数作为参数,并返回一个新的函数。
实例
def my_decorator(func):
def wrapper(*args, **kwargs):
print("函数执行前的一些操作")
result = func(*args, **kwargs)
print("函数执行后的一些操作")
return result
return wrapper
@my_decorator
def say_hello(name):
print(f"Hello, {name}!")
# 使用装饰器
say_hello("Alice")
def wrapper(*args, **kwargs):
print("函数执行前的一些操作")
result = func(*args, **kwargs)
print("函数执行后的一些操作")
return result
return wrapper
@my_decorator
def say_hello(name):
print(f"Hello, {name}!")
# 使用装饰器
say_hello("Alice")
输出结果:
函数执行前的一些操作 Hello, Alice! 函数执行后的一些操作
带参数的装饰器
如果需要向装饰器传递参数,需要再嵌套一层函数:
实例
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(times):
print(f"第 {i+1} 次执行:")
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"你好, {name}!")
greet("Bob")
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(times):
print(f"第 {i+1} 次执行:")
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"你好, {name}!")
greet("Bob")
类装饰器
除了函数装饰器,Python 还支持类装饰器。类装饰器通过实现 __call__ 方法来工作。
基本的类装饰器
实例
class TimerDecorator:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
import time
start_time = time.time()
result = self.func(*args, **kwargs)
end_time = time.time()
print(f"函数 {self.func.__name__} 执行耗时: {end_time - start_time:.4f} 秒")
return result
@TimerDecorator
def calculate_sum(n):
return sum(range(n))
result = calculate_sum(1000000)
print(f"计算结果: {result}")
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
import time
start_time = time.time()
result = self.func(*args, **kwargs)
end_time = time.time()
print(f"函数 {self.func.__name__} 执行耗时: {end_time - start_time:.4f} 秒")
return result
@TimerDecorator
def calculate_sum(n):
return sum(range(n))
result = calculate_sum(1000000)
print(f"计算结果: {result}")
带参数的类装饰器
实例
class LogDecorator:
def __init__(self, level="INFO"):
self.level = level
def __call__(self, func):
def wrapper(*args, **kwargs):
print(f"[{self.level}] 调用函数: {func.__name__}")
print(f"[{self.level}] 参数: args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"[{self.level}] 返回值: {result}")
return result
return wrapper
@LogDecorator(level="DEBUG")
def multiply(a, b):
return a * b
multiply(5, 3)
def __init__(self, level="INFO"):
self.level = level
def __call__(self, func):
def wrapper(*args, **kwargs):
print(f"[{self.level}] 调用函数: {func.__name__}")
print(f"[{self.level}] 参数: args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"[{self.level}] 返回值: {result}")
return result
return wrapper
@LogDecorator(level="DEBUG")
def multiply(a, b):
return a * b
multiply(5, 3)
内置装饰器
Python 提供了一些有用的内置装饰器:
@staticmethod 和 @classmethod
实例
class Calculator:
@staticmethod
def add(x, y):
return x + y
@classmethod
def multiply(cls, x, y):
return x * y
# 使用静态方法
result1 = Calculator.add(5, 3)
print(f"静态方法结果: {result1}")
# 使用类方法
result2 = Calculator.multiply(5, 3)
print(f"类方法结果: {result2}")
@staticmethod
def add(x, y):
return x + y
@classmethod
def multiply(cls, x, y):
return x * y
# 使用静态方法
result1 = Calculator.add(5, 3)
print(f"静态方法结果: {result1}")
# 使用类方法
result2 = Calculator.multiply(5, 3)
print(f"类方法结果: {result2}")
@property
实例
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value <= 0:
raise ValueError("半径必须为正数")
self._radius = value
@property
def area(self):
return 3.14159 * self._radius ** 2
circle = Circle(5)
print(f"半径: {circle.radius}")
print(f"面积: {circle.area}")
circle.radius = 10
print(f"新半径: {circle.radius}")
print(f"新面积: {circle.area}")
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value <= 0:
raise ValueError("半径必须为正数")
self._radius = value
@property
def area(self):
return 3.14159 * self._radius ** 2
circle = Circle(5)
print(f"半径: {circle.radius}")
print(f"面积: {circle.area}")
circle.radius = 10
print(f"新半径: {circle.radius}")
print(f"新面积: {circle.area}")
装饰器的实际应用场景
1. 日志记录
实例
def log_execution(func):
def wrapper(*args, **kwargs):
print(f"开始执行: {func.__name__}")
try:
result = func(*args, **kwargs)
print(f"成功完成: {func.__name__}")
return result
except Exception as e:
print(f"执行失败: {func.__name__}, 错误: {e}")
raise
return wrapper
@log_execution
def process_data(data):
# 模拟数据处理
if not data:
raise ValueError("数据不能为空")
return [x * 2 for x in data]
# 测试
data = [1, 2, 3]
result = process_data(data)
print(f"处理结果: {result}")
def wrapper(*args, **kwargs):
print(f"开始执行: {func.__name__}")
try:
result = func(*args, **kwargs)
print(f"成功完成: {func.__name__}")
return result
except Exception as e:
print(f"执行失败: {func.__name__}, 错误: {e}")
raise
return wrapper
@log_execution
def process_data(data):
# 模拟数据处理
if not data:
raise ValueError("数据不能为空")
return [x * 2 for x in data]
# 测试
data = [1, 2, 3]
result = process_data(data)
print(f"处理结果: {result}")
2. 权限验证
实例
def require_login(func):
def wrapper(user, *args, **kwargs):
if not user.get('is_authenticated', False):
raise PermissionError("用户未登录")
return func(user, *args, **kwargs)
return wrapper
@require_login
def view_profile(user):
return f"查看用户 {user['username']} 的个人资料"
# 测试
user1 = {'username': 'alice', 'is_authenticated': True}
user2 = {'username': 'bob', 'is_authenticated': False}
print(view_profile(user1)) # 正常执行
# print(view_profile(user2)) # 会抛出 PermissionError
def wrapper(user, *args, **kwargs):
if not user.get('is_authenticated', False):
raise PermissionError("用户未登录")
return func(user, *args, **kwargs)
return wrapper
@require_login
def view_profile(user):
return f"查看用户 {user['username']} 的个人资料"
# 测试
user1 = {'username': 'alice', 'is_authenticated': True}
user2 = {'username': 'bob', 'is_authenticated': False}
print(view_profile(user1)) # 正常执行
# print(view_profile(user2)) # 会抛出 PermissionError
3. 缓存装饰器
实例
def cache_results(func):
cache = {}
def wrapper(*args):
if args in cache:
print(f"从缓存中获取结果: {args}")
return cache[args]
result = func(*args)
cache[args] = result
print(f"计算并缓存结果: {args} -> {result}")
return result
return wrapper
@cache_results
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 测试缓存效果
print(fibonacci(5))
print(fibonacci(5)) # 这次会从缓存中获取
cache = {}
def wrapper(*args):
if args in cache:
print(f"从缓存中获取结果: {args}")
return cache[args]
result = func(*args)
cache[args] = result
print(f"计算并缓存结果: {args} -> {result}")
return result
return wrapper
@cache_results
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 测试缓存效果
print(fibonacci(5))
print(fibonacci(5)) # 这次会从缓存中获取
多个装饰器的执行顺序
当使用多个装饰器时,它们的执行顺序是从下往上:
实例
def decorator1(func):
def wrapper():
print("装饰器1 - 前")
func()
print("装饰器1 - 后")
return wrapper
def decorator2(func):
def wrapper():
print("装饰器2 - 前")
func()
print("装饰器2 - 后")
return wrapper
@decorator1
@decorator2
def my_function():
print("原始函数")
my_function()
def wrapper():
print("装饰器1 - 前")
func()
print("装饰器1 - 后")
return wrapper
def decorator2(func):
def wrapper():
print("装饰器2 - 前")
func()
print("装饰器2 - 后")
return wrapper
@decorator1
@decorator2
def my_function():
print("原始函数")
my_function()
输出结果:
装饰器1 - 前 装饰器2 - 前 原始函数 装饰器2 - 后 装饰器1 - 后
保留函数元信息
使用装饰器时,原始函数的元信息(如函数名、文档字符串等)会被包装函数覆盖。可以使用 functools.wraps 来保留这些信息:
实例
from functools import wraps
def preserve_metadata(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""包装函数的文档字符串"""
return func(*args, **kwargs)
return wrapper
@preserve_metadata
def example_function():
"""这是原始函数的文档字符串"""
pass
print(f"函数名: {example_function.__name__}")
print(f"文档字符串: {example_function.__doc__}")
def preserve_metadata(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""包装函数的文档字符串"""
return func(*args, **kwargs)
return wrapper
@preserve_metadata
def example_function():
"""这是原始函数的文档字符串"""
pass
print(f"函数名: {example_function.__name__}")
print(f"文档字符串: {example_function.__doc__}")
实践练习
练习 1:创建性能监控装饰器
编写一个装饰器,用于监控函数的执行时间和内存使用情况。
实例
import time
import tracemalloc
def performance_monitor(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 开始内存跟踪
tracemalloc.start()
# 记录开始时间
start_time = time.time()
# 执行函数
result = func(*args, **kwargs)
# 记录结束时间
end_time = time.time()
# 获取内存使用情况
current, peak = tracemalloc.get_traced_memory()
tracemalloc.stop()
print(f"函数 {func.__name__} 性能报告:")
print(f"执行时间: {end_time - start_time:.4f} 秒")
print(f"内存使用: 当前 {current/1024:.2f} KB, 峰值 {peak/1024:.2f} KB")
return result
return wrapper
@performance_monitor
def process_large_data():
"""模拟处理大量数据"""
data = [i**2 for i in range(100000)]
return sum(data)
process_large_data()
import tracemalloc
def performance_monitor(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 开始内存跟踪
tracemalloc.start()
# 记录开始时间
start_time = time.time()
# 执行函数
result = func(*args, **kwargs)
# 记录结束时间
end_time = time.time()
# 获取内存使用情况
current, peak = tracemalloc.get_traced_memory()
tracemalloc.stop()
print(f"函数 {func.__name__} 性能报告:")
print(f"执行时间: {end_time - start_time:.4f} 秒")
print(f"内存使用: 当前 {current/1024:.2f} KB, 峰值 {peak/1024:.2f} KB")
return result
return wrapper
@performance_monitor
def process_large_data():
"""模拟处理大量数据"""
data = [i**2 for i in range(100000)]
return sum(data)
process_large_data()
练习 2:实现重试机制装饰器
创建一个装饰器,在函数执行失败时自动重试指定次数。
实例
def retry(max_attempts=3, delay=1):
def decorator(func):
@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:
print(f"函数 {func.__name__} 执行失败,已达到最大重试次数")
raise
print(f"函数 {func.__name__} 第 {attempts} 次执行失败: {e}")
print(f"等待 {delay} 秒后重试...")
time.sleep(delay)
return None
return wrapper
return decorator
@retry(max_attempts=3, delay=2)
def unstable_operation():
"""模拟不稳定的操作"""
import random
if random.random() < 0.7: # 70% 的概率失败
raise ValueError("随机失败")
return "操作成功"
# 测试重试机制
result = unstable_operation()
print(f"最终结果: {result}")
def decorator(func):
@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:
print(f"函数 {func.__name__} 执行失败,已达到最大重试次数")
raise
print(f"函数 {func.__name__} 第 {attempts} 次执行失败: {e}")
print(f"等待 {delay} 秒后重试...")
time.sleep(delay)
return None
return wrapper
return decorator
@retry(max_attempts=3, delay=2)
def unstable_operation():
"""模拟不稳定的操作"""
import random
if random.random() < 0.7: # 70% 的概率失败
raise ValueError("随机失败")
return "操作成功"
# 测试重试机制
result = unstable_operation()
print(f"最终结果: {result}")
总结
Python 装饰器是一个强大而灵活的工具,它提供了以下优势:
- 代码复用:将横切关注点(如日志、缓存、验证)从业务逻辑中分离
- 动态扩展:在不修改原始代码的情况下添加新功能
- 保持简洁:使用
@语法让代码更加清晰易读 - 符合开闭原则:对扩展开放,对修改关闭
使用装饰器的最佳实践:
- 使用
functools.wraps保留函数元信息 - 保持装饰器的单一职责
- 合理处理装饰器中的异常
- 注意装饰器的执行顺序
点我分享笔记