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. 子类爆炸问题对比

如果使用继承来扩展咖啡功能,每种组合都需要一个子类,导致子类数量爆炸:

对比项 继承方式 装饰器模式
类的数量 2^n(n为特性数) n+1(n个装饰器+1个基类)
运行时灵活性 编译时确定,不可变 运行时动态组合
代码复用 差(大量重复代码) 好(装饰器可复用)
开闭原则 不遵守 遵守

三、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语法和设计模式中的装饰器模式虽然名字相同,但它们的关注点有所不同。

对比项 GOF装饰器模式 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中,这种开销通常可以忽略不计。只有在对性能极度敏感的场景(如每秒百万次调用)时才需要考虑优化。

装饰器顺序有影响吗?

有。装饰器的应用顺序会影响最终结果。对于函数装饰器,@装饰器的执行顺序是从下到上(靠近函数的先执行);对于装饰器模式,嵌套顺序决定了数据流的处理顺序。设计时需要考虑装饰器的执行顺序。

标签: Python 装饰器模式 动态扩展 Python装饰器 中间件 面向组合

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: Python策略模式完全指南 - 消除条件分支的最佳实践 下一篇: Python unittest 框架完整教程 - 从入门到精通

poll相关推荐