pin_drop当前位置:知识文库 ❯ 图文
Python装饰器模式完全指南 - 动态扩展与继承对比
一、什么是装饰器模式
装饰器模式(Decorator Pattern)是结构型设计模式中的一种。它允许在不修改原始对象的情况下,动态地给对象添加新的功能和职责。
装饰器模式通过组合而非继承来扩展功能,避免了子类爆炸的问题。它遵循开闭原则,对扩展开放,对修改关闭。
装饰器模式的核心概念
-
Component(组件):定义可以动态添加职责的对象接口
-
ConcreteComponent(具体组件):被装饰的原始对象
-
Decorator(装饰器):持有组件引用,并实现与组件相同的接口
-
动态扩展:可以在运行时组合多个装饰器
二、装饰器模式的基础实现
1. 经典实现 - 咖啡加料
代码示例
from abc import ABC, abstractmethod
# 组件接口
class Beverage(ABC):
@abstractmethod
def cost(self) -> float:
pass
@abstractmethod
def description(self) -> str:
pass
# 具体组件
class Coffee(Beverage):
def cost(self) -> float:
return 15.0
def description(self) -> str:
return "原味咖啡"
class Tea(Beverage):
def cost(self) -> float:
return 10.0
def description(self) -> str:
return "原味茶"
# 装饰器基类
class BeverageDecorator(Beverage):
def __init__(self, beverage: Beverage):
self._beverage = beverage
def description(self) -> str:
return self._beverage.description()
# 具体装饰器
class MilkDecorator(BeverageDecorator):
def cost(self) -> float:
return self._beverage.cost() + 5.0
def description(self) -> str:
return super().description() + " + 牛奶"
class SugarDecorator(BeverageDecorator):
def cost(self) -> float:
return self._beverage.cost() + 2.0
def description(self) -> str:
return super().description() + " + 糖"
class VanillaDecorator(BeverageDecorator):
def cost(self) -> float:
return self._beverage.cost() + 3.0
def description(self) -> str:
return super().description() + " + 香草"
class IceDecorator(BeverageDecorator):
def cost(self) -> float:
return self._beverage.cost() + 1.0
def description(self) -> str:
return super().description() + " + 冰"
# 使用 - 自由组合装饰器
coffee = Coffee()
print(f"{coffee.description()}: {coffee.cost()}元")
# 加牛奶和糖
coffee_with_milk_sugar = SugarDecorator(MilkDecorator(coffee))
print(f"{coffee_with_milk_sugar.description()}: {coffee_with_milk_sugar.cost()}元")
# 冰香草拿铁
iced_vanilla_latte = IceDecorator(VanillaDecorator(MilkDecorator(Coffee())))
print(f"{iced_vanilla_latte.description()}: {iced_vanilla_latte.cost()}元")
# 冰加糖红茶
iced_sugar_tea = IceDecorator(SugarDecorator(Tea()))
print(f"{iced_sugar_tea.description()}: {iced_sugar_tea.cost()}元")
2. 子类爆炸问题对比
如果使用继承来扩展咖啡功能,每种组合都需要一个子类,导致子类数量爆炸:
三、Python装饰器语法
Python的@decorator语法是装饰器模式的语法糖,但它更偏向于函数级别的包装,与GOF装饰器模式有所不同。
1. 函数装饰器基础
代码示例
import functools
import time
def timer(func):
"""计时装饰器"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - start
print(f"{func.__name__} 执行时间: {elapsed*1000:.2f}ms")
return result
return wrapper
def log_calls(func):
"""日志装饰器"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
args_str = ", ".join(map(str, args))
print(f"调用 {func.__name__}({args_str})")
result = func(*args, **kwargs)
print(f"{func.__name__} 返回: {result}")
return result
return wrapper
def retry(max_attempts=3):
"""重试装饰器(带参数)"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"第{attempt+1}次尝试失败: {e}")
if attempt == max_attempts - 1:
raise
time.sleep(1)
return wrapper
return decorator
# 使用装饰器
@timer
@log_calls
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
result = fibonacci(10)
print(f"结果: {result}")
2. 类装饰器
代码示例
import functools
class CountCalls:
"""统计函数调用次数的类装饰器"""
def __init__(self, func):
functools.update_wrapper(self, func)
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"{self.func.__name__} 被调用了 {self.count} 次")
return self.func(*args, **kwargs)
@CountCalls
def say_hello(name):
print(f"Hello, {name}!")
say_hello("Alice")
say_hello("Bob")
say_hello("Charlie")
print(f"总调用次数: {say_hello.count}")
四、装饰器模式 vs Python装饰器
Python的@decorator语法和设计模式中的装饰器模式虽然名字相同,但它们的关注点有所不同。
五、装饰器模式的进阶用法
1. 使用__getattr__实现通用装饰器
代码示例
class TextSource:
"""文本源 - 具体组件"""
def __init__(self, text):
self.text = text
def get_content(self):
return self.text
def get_length(self):
return len(self.text)
class TextDecorator:
"""通用文本装饰器"""
def __init__(self, source):
self._source = source
def __getattr__(self, name):
# 将未实现的方法委托给源对象
return getattr(self._source, name)
class UppercaseDecorator(TextDecorator):
def get_content(self):
return self._source.get_content().upper()
class HTMLDecorator(TextDecorator):
def get_content(self):
return f"<p>{self._source.get_content()}</p>"
class WordWrapDecorator(TextDecorator):
def __init__(self, source, width=40):
super().__init__(source)
self.width = width
def get_content(self):
content = self._source.get_content()
words = content.split()
lines = []
current_line = []
current_length = 0
for word in words:
if current_length + len(word) + 1 > self.width:
lines.append(" ".join(current_line))
current_line = [word]
current_length = len(word)
else:
current_line.append(word)
current_length += len(word) + 1
if current_line:
lines.append(" ".join(current_line))
return "\n".join(lines)
# 使用
text = TextSource("Python装饰器模式是一种强大的设计模式,它允许我们动态地为对象添加新功能而不修改原始代码。这种模式在很多场景下都非常有用。")
# 自由组合装饰器
wrapped = WordWrapDecorator(text, width=30)
html = HTMLDecorator(wrapped)
upper = UppercaseDecorator(html)
print(upper.get_content())
# 仍然可以调用原始方法
print(f"长度: {upper.get_length()}")
2. 可组合的装饰器类
代码示例
from abc import ABC, abstractmethod
class Transformer(ABC):
"""数据转换器接口"""
@abstractmethod
def transform(self, data):
pass
def __add__(self, other):
"""支持装饰器组合: t1 + t2"""
return CompositeTransformer(self, other)
class CompositeTransformer(Transformer):
"""组合转换器"""
def __init__(self, *transformers):
self._transformers = list(transformers)
def __add__(self, other):
self._transformers.append(other)
return self
def transform(self, data):
result = data
for t in self._transformers:
result = t.transform(result)
return result
class StripTransformer(Transformer):
def transform(self, data):
return [line.strip() for line in data]
class FilterEmptyTransformer(Transformer):
def transform(self, data):
return [line for line in data if line]
class NumberTransformer(Transformer):
def transform(self, data):
return [f"{i+1}. {line}" for i, line in enumerate(data)]
class SortTransformer(Transformer):
def transform(self, data):
return sorted(data)
# 使用:像搭积木一样组合转换器
pipeline = StripTransformer() + FilterEmptyTransformer() + NumberTransformer()
data = [
" banana ",
"",
" apple ",
" ",
" cherry ",
" date ",
]
result = pipeline.transform(data)
for line in result:
print(line)
六、实际应用场景
1. HTTP请求中间件
代码示例
class Request:
def __init__(self, url, headers=None, body=None):
self.url = url
self.headers = headers or {}
self.body = body
class Response:
def __init__(self, status, body):
self.status = status
self.body = body
class Middleware(ABC):
def __init__(self, next_handler):
self._next = next_handler
def handle(self, request):
return self._next.handle(request)
class AuthMiddleware(Middleware):
def handle(self, request):
if "Authorization" not in request.headers:
return Response(401, "未授权")
if not request.headers["Authorization"].startswith("Bearer "):
return Response(403, "令牌无效")
return super().handle(request)
class LoggingMiddleware(Middleware):
def handle(self, request):
print(f"[请求] {request.url}")
response = super().handle(request)
print(f"[响应] {response.status}")
return response
class RateLimitMiddleware(Middleware):
def __init__(self, next_handler, max_requests=100):
super().__init__(next_handler)
self._requests = 0
self._max = max_requests
def handle(self, request):
self._requests += 1
if self._requests > self._max:
return Response(429, "请求过于频繁")
return super().handle(request)
class FinalHandler:
"""最终处理器"""
def handle(self, request):
return Response(200, f"处理了请求: {request.url}")
# 构建中间件链
handler = AuthMiddleware(
RateLimitMiddleware(
LoggingMiddleware(
FinalHandler()
)
)
)
# 测试
req1 = Request("/api/users", {"Authorization": "Bearer token123"})
resp = handler.handle(req1)
print(f"状态: {resp.status}, 内容: {resp.body}")
req2 = Request("/api/admin") # 没有授权
resp2 = handler.handle(req2)
print(f"状态: {resp2.status}, 内容: {resp2.body}")
2. 数据验证管道
代码示例
class Validator:
"""验证器装饰器"""
def __init__(self, next_validator=None):
self._next = next_validator
def validate(self, value):
if self._next:
return self._next.validate(value)
return value, []
class NotNullValidator(Validator):
def validate(self, value):
if value is None:
return value, ["值不能为空"]
return super().validate(value)
class MinLengthValidator(Validator):
def __init__(self, min_len, next_validator=None):
super().__init__(next_validator)
self.min_len = min_len
def validate(self, value):
if value and len(str(value)) < self.min_len:
return value, [f"长度不能少于{self.min_len}"]
return super().validate(value)
class MaxLengthValidator(Validator):
def __init__(self, max_len, next_validator=None):
super().__init__(next_validator)
self.max_len = max_len
def validate(self, value):
if value and len(str(value)) > self.max_len:
return value, [f"长度不能超过{self.max_len}"]
return super().validate(value)
class EmailFormatValidator(Validator):
def validate(self, value):
import re
if value and not re.match(r'^[\w\.-]+@[\w\.-]+\.\w+$', str(value)):
return value, ["邮箱格式不正确"]
return super().validate(value)
class ValidationPipeline:
def __init__(self, *validators):
# 从后往前构建链
pipeline = Validator()
for v in reversed(validators):
v._next = pipeline
pipeline = v
self._pipeline = pipeline
def validate(self, value):
return self._pipeline.validate(value)
# 使用
email_validator = ValidationPipeline(
NotNullValidator(),
MinLengthValidator(5),
MaxLengthValidator(100),
EmailFormatValidator()
)
test_emails = [
"user@example.com",
None,
"invalid",
"a@b.c",
"valid.email@domain.org"
]
for email in test_emails:
value, errors = email_validator.validate(email)
status = "通过" if not errors else f"失败: {errors}"
print(f"{email!r} → {status}")
小贴士
Python的functools模块提供了@functools.wraps装饰器,用于保留被装饰函数的元信息(如__name__、__doc__等)。在编写装饰器时,强烈建议使用它来避免调试时的混淆。此外,functools.lru_cache本身就是一个实用的装饰器,可以为函数添加缓存功能。
七、小结与练习题
核心要点总结
-
装饰器模式:通过组合动态扩展对象功能,避免子类爆炸
-
Python装饰器:@语法糖用于函数/类级别的静态包装,侧重横切关注点
-
与继承对比:装饰器提供运行时灵活性,继承提供编译时确定性
-
最佳实践:使用__getattr__实现透明委托,使用functools.wraps保留元信息
练习题
练习1
实现一个图像处理装饰器系统,支持模糊、锐化、旋转、缩放等操作的自由组合。使用装饰器模式让每种效果可以独立添加和组合。
练习2
设计一个API响应装饰器链,支持缓存、压缩(gzip)、CORS头添加、速率限制等功能。在实际场景中应用装饰器模式处理HTTP响应。
常见问题
装饰器模式和Python的@装饰器语法是一回事吗?
不完全相同。GOF装饰器模式是面向对象的设计模式,关注运行时对象的动态扩展;Python的@装饰器是语言级别的语法特性,主要用于函数/类的静态包装。两者思想相通(都是包装),但应用场景和实现方式不同。
什么时候用装饰器模式,什么时候用继承?
当功能扩展是静态的、不会在运行时变化的,用继承更简单;当需要在运行时动态组合多个功能,或者功能组合数量很多(导致子类爆炸)时,用装饰器模式更合适。
装饰器模式会影响性能吗?
每次方法调用会增加一层方法转发,理论上会有轻微性能损耗。但在Python中,这种开销通常可以忽略不计。只有在对性能极度敏感的场景(如每秒百万次调用)时才需要考虑优化。
装饰器顺序有影响吗?
有。装饰器的应用顺序会影响最终结果。对于函数装饰器,@装饰器的执行顺序是从下到上(靠近函数的先执行);对于装饰器模式,嵌套顺序决定了数据流的处理顺序。设计时需要考虑装饰器的执行顺序。
本文涉及AI创作
内容由AI创作,请仔细甄别