Python 设计模式全解析

刚从其他静态语言跳Python的朋友,是不是一翻设计模式就觉得:这怎么能薄一半、还能玩出“花活”?

设计模式是针对软件设计中常见问题的通用解决方案。在 Python 中,我们利用其动态特性,可以用比 Java 更简洁的代码实现这些模式。
👉 完整Python设计模式代码集:faif/python-patterns


一、 创建型模式 (Creational Patterns)

核心目标: 把「谁来创建对象」「怎么创建」「用什么代表」三件事,和系统的业务逻辑调用方彻底分开——这样改个配置换个实现,调用方一行都不用碰。

1. 工厂方法 (Factory Method)

把“创建对象”的逻辑单独包装成函数/类,给调用方留个「按需求取货」的入口就行。

Python极简实现

这里连类都省了,直接用函数+闭包/内置类完成:

def get_serializer(fmt: str):
    """根据格式返回对应的序列化工具"""
    if fmt == "json":
        return lambda data: f"JSON: {str(data).replace(' ', '')}"
    if fmt == "xml":
        return lambda data: f"<xml>{data}</xml>"
    # 默认返回字符串转义版?不,直接原生str更灵活
    return str

# 业务调用方的代码
if __name__ == "__main__":
    user = {"name": "Alice", "age": 28}
    # 只传格式参数,完全不知道内部用了lambda还是str
    json_serializer = get_serializer("json")
    print(json_serializer(user))

优缺点与适用

优点: 屏蔽细节,新增格式(比如yaml)只需要在工厂里加个if,调用方零修改;
缺点: 简单场景(比如永远只返回json)会显得“小题大做”;
🎯 场景: 根据配置/用户输入/环境变量选实例、API响应格式切换。


2. 抽象工厂 (Abstract Factory)

工厂方法的“升级版工厂”——管理一组「互相关联、必须一起用」的对象(比如“暗黑模式”家族的背景色、文字色、按钮样式)。

Python极简实现

利用Python的动态特性,连“抽象基类”都可以简化(当然实际大项目还是建议加abc模块约束):

# 定义两个「产品族」的成员类
class DarkComponent:
    get_bg = lambda self: "#000000"
    get_text = lambda self: "#FFFFFF"

class LightComponent:
    get_bg = lambda self: "#FFFFFF"
    get_text = lambda self: "#000000"

# 抽象工厂(统一返回家族成员的入口)
def get_theme_factory(mode: str):
    if mode == "dark":
        return DarkComponent
    if mode == "light":
        return LightComponent
    raise ValueError(f"不支持的主题模式: {mode}")

# 业务调用方
if __name__ == "__main__":
    # 选一个模式,拿到整个家族的工厂
    theme_factory = get_theme_factory("dark")
    # 直接取家族里的所有关联组件,绝对不会出现「暗黑背景配亮瞎眼的黑字」
    print(f"背景色: {theme_factory.get_bg()}, 文字色: {theme_factory.get_text()}")

优缺点与适用

优点: 强制产品家族一起使用,风格/功能绝对统一;
缺点: 扩展「单个新产品」还好,扩展「整个新产品族」(比如加个粉紫渐变主题)要改所有工厂代码;
🎯 场景: UI换肤系统、多数据库(MySQL/PostgreSQL/Oracle)驱动全家桶、多语言翻译组件。


3. 惰性初始化 (Lazy Evaluation)

对象/数据不提前准备好,第一次用的时候才加载——特别适合“内存/资源很贵,可能永远用不上”的东西。

Python极简实现

@property装饰器把加载逻辑藏在“属性访问”的背后:

class LargeFileLoader:
    def __init__(self, file_path: str):
        self.file_path = file_path
        # 提前占位,不真的加载
        self._content = None

    @property
    def content(self):
        """第一次访问content属性时才加载文件"""
        if self._content is None:
            print(f"⏳ 首次加载大文件: {self.file_path}...")
            # 真实场景用open,这里模拟
            self._content = ["行1", "行2", "行3", "行..."] * 10000
        return self._content

# 业务调用方
if __name__ == "__main__":
    loader = LargeFileLoader("超大日志.txt")
    # 此时还没加载!!
    print("对象初始化完成,文件未加载")
    # 第一次print才会触发加载
    print(f"文件内容前2行: {loader.content[:2]}")
    # 第二次直接用缓存,不会再加载
    print(f"文件总行数(复用缓存): {len(loader.content)}")

优缺点与适用

优点: 减少启动时间、降低初始内存占用;
缺点: 第一次访问会有明显延迟、线程不安全(多线程下可能同时加载两次);
🎯 场景: 加载超大文件、建立昂贵的数据库/网络连接、解析复杂的配置文件。


4. 单例模式 (Singleton)

确保一个类在整个程序生命周期里只有一个实例——适合需要“全局共享唯一状态”的东西。

Python极简(但安全隐患少)的实现

用元类的方式(比__new__更“高级”但更可控,避免多线程和继承问题的简化版):

class SingletonMeta(type):
    """元类:控制类的创建过程"""
    _instances = {}

    def __call__(cls, *args, **kwargs):
        """当调用类(比如AppConfig())时触发"""
        if cls not in cls._instances:
            # 先占个位置?或者直接加锁?这里是简化版单线程
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

# 实际的单例类
class AppConfig(metaclass=SingletonMeta):
    def __init__(self):
        # 这里的初始化只会在第一次调用时执行!!
        print("🔧 首次加载配置...")
        self.db_host = "localhost"
        self.db_port = 3306

# 测试单例
if __name__ == "__main__":
    config1 = AppConfig()
    config2 = AppConfig()
    # 两个变量指向同一个对象
    print(f"config1 == config2? {config1 == config2}")
    print(f"config1的地址: {id(config1)}, config2的地址: {id(config2)}")

优缺点与适用

优点: 节约内存、保证全局状态唯一、避免重复初始化;
缺点: 违背单一职责原则(一个类既要管自己的业务,还要管单例)、测试困难(状态共享容易脏测试)、多线程需加锁;
🎯 场景: 全局配置管理、数据库连接池、日志记录器。


二、 结构型模式 (Structural Patterns)

核心目标:类继承/对象组合的方式,把零散的小类/对象“拼”成更大、更灵活的结构——不用改原有代码,就能加功能、换结构。

5. 装饰器模式 (Decorator)

动态地给对象/函数「叠加」额外功能——比“继承子类加功能”灵活100倍,因为可以任意顺序叠加多个装饰器

Python原生支持!极简实现

Python自带@语法糖,直接用函数/类装饰器就行:

# 定义两个装饰器
def bold(func):
    """给返回值加<b>标签"""
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return f"<b>{result}</b>"
    return wrapper

def italic(func):
    """给返回值加<i>标签"""
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return f"<i>{result}</i>"
    return wrapper

# 业务函数,用@语法糖叠加装饰器(注意顺序:从下往上生效)
@bold
@italic
def say_hello(name: str):
    return f"Hello, {name}!"

# 测试
if __name__ == "__main__":
    print(say_hello("Bob"))  # 输出: <b><i>Hello, Bob!</i></b>

优缺点与适用

优点: 动态叠加、不修改原有代码、符合“开闭原则”;
缺点: 多个嵌套装饰器会增加调试难度、可能改变原函数的元信息(比如__name__,可以用functools.wraps解决);
🎯 场景: 日志记录、性能监控、权限校验、缓存预热。


三、 行为型模式 (Behavioral Patterns)

核心目标: 关注「对象之间怎么沟通」「责任怎么分配」——让沟通更清晰、责任更明确、代码更易扩展。

6. 迭代器模式 (Iterator)

访问集合/容器里的元素,但完全不暴露容器的内部结构——Python原生几乎所有容器都支持!

Python原生用法 + 自定义简单迭代器

# 1. 原生用法(Python内置容器全支持,比如list/dict/set/str)
if __name__ == "__main__":
    print("--- 原生list迭代器 ---")
    my_list = [10, 20, 30]
    # iter()拿到迭代器对象
    it = iter(my_list)
    # next()逐个取元素,取完会抛StopIteration异常
    print(next(it))  # 10
    print(next(it))  # 20
    print(next(it))  # 30
    # print(next(it))  # 这里会报错

# 2. 自定义简单迭代器(实现__iter__和__next__)
class EvenNumberIterator:
    """生成0到max_num的所有偶数的迭代器"""
    def __init__(self, max_num: int):
        self.max_num = max_num
        self.current = 0

    def __iter__(self):
        """必须返回自己,因为迭代器本身也是可迭代对象"""
        return self

    def __next__(self):
        """返回下一个偶数,超过max_num就抛异常"""
        if self.current > self.max_num:
            raise StopIteration
        result = self.current
        self.current += 2
        return result

# 测试自定义迭代器
if __name__ == "__main__":
    print("\n--- 自定义偶数迭代器 ---")
    even_it = EvenNumberIterator(10)
    # 可以直接用for循环(Python的for本质上就是调用iter()和next())
    for num in even_it:
        print(num)

优缺点与适用

优点: 统一遍历接口、不暴露内部结构、支持多种遍历方式;
缺点: 对于简单的list/dict,手动实现迭代器显得多余;
🎯 场景: 遍历数据库大表记录(避免一次性加载到内存)、处理大型流式文件、自定义复杂的数据结构。