人人都会AI编程

28.3 设计模式在 Python 中的实现与选型

更新时间:2026-07-12

设计模式是前人总结的、针对特定问题的可复用解决方案。但在 Python 里谈设计模式,常常会遇到一种微妙的反差:经典《设计模式》一书中的 23 种模式,不少是用 Java/C++ 那样的静态类型、显式接口语言来阐述的;而 Python 的动态类型、一等函数、装饰器、元类等特性,会让很多模式的实现变得极简甚至不必要。所以这一节的核心不是教条地背诵模式,而是弄清楚:在 Python 里怎样实现这些模式,以及什么时候该用、什么时候该丢。

为什么 Python 的设计模式感觉不一样

  • 动态类型与鸭子类型:不需要定义抽象基类来保证接口统一,只要对象有需要的方法就能正常工作,这使得“策略模式”“命令模式”等很多依赖接口的模式退化成了简单的函数传递。
  • 一等函数:函数本身可以作为参数、返回值、存储在容器中,直接替代很多为了包装对象而生的模式(如命令、策略)。
  • 装饰器:语言层面的装饰器天然对应装饰器模式,且比经典实现更轻量。
  • 元类/描述符:可以轻松做到类级别的行为控制,很多涉及类创建、属性访问的模式直接用元类或描述符实现。

因此,在用 Python 实现设计模式时,首要原则是:先问自己“这个问题在 Python 里有没有更简单的写法”,而不是机械地搬用 Java/C++ 的结构。

常见模式在 Python 中的实际写法

单例模式
目标是确保一个类只有一个实例。Python 有多种实现方式:

  • 使用模块:最简单,因为模块在程序运行期间本身就是单例。直接在模块里定义全局变量或实例,各导入点共享。
  • 重写 new:控制实例化过程,经典写法。
  • 使用装饰器或元类:可以做成可复用的单例约束,但略显小题大做。

实际项目里,如果只是需要共享某个配置对象或数据库连接池,模块级变量通常就够了,无需刻意实现单例类。

工厂模式

  • 简单工厂/工厂方法:Python 里常退化为一个函数,根据参数返回不同类的实例。由于类本身也是对象,可以直接用字典将字符串映射到类,再用 cls() 实例化。比经典的抽象工厂接口灵活得多。
  • 动态导入:可以通过 importlib 根据配置字符串动态加载类,无需维护庞大的 if-else 判断。

策略模式
定义一系列算法,使得它们可以相互替换。在 Python 里,策略往往就是一个可调用对象

  • 直接传递函数,或者带有 call 方法的类实例。
  • 通过 lambdaoperator 模块可以实现极简的策略切换。

例如,排序时给 sorted 传递不同的 key 函数,本质上就是策略模式。

观察者模式/发布-订阅

  • Python 标准库没有内建的观察者,但可以通过 weakref 和一些容器类轻松实现。
  • 许多框架(如 Django 的信号、blinker 库)提供了成熟的信号机制,优于自己从头实现。
  • 在异步场景下,使用 asyncio.Queue 或第三方消息队列也是变体的观察者/发布订阅。

装饰器模式
动态地给对象添加额外职责。Python 的装饰器语法糖直接实现了装饰器模式:

  • 函数装饰器:在函数调用前后增加日志、计时、权限检查等。
  • 类装饰器:修改或增强类的行为。
  • 通过 functools.wraps 保持元数据。

当需要为某个对象灵活叠加多个功能时,装饰器是最自然的选择。但过度嵌套装饰器会降低可读性,要权衡。

适配器模式
将不兼容的接口转换为期望的接口。Python 中实现适配器往往只需定义一个包装类,内部持有原对象,在方法中转换调用即可。由于鸭子类型的存在,许多情况下甚至不需要显式包装,直接赋予对象新的方法(猴子补丁)就能充当适配器,但需谨慎使用以免影响全局。

模板方法模式
在基类中定义算法骨架,将步骤延迟到子类实现。Python 的做法很直接:基类中用抽象方法或引发 NotImplementedError 的方法作为“步骤”,子类重写即可。也可以使用 abc.ABC@abstractmethod 提供明确接口约束。在数据科学流水线中,这种模式常用于定义 ETL(抽取、转换、加载)框架。

代理模式
控制对其他对象的访问。常见实现是定义 getattr 将未定义的属性访问转发给内部对象。这在延迟加载、权限校验、远程调用等场景中很实用。

选型原则:用 Python 的方式思考

  • 模式不是目标,解决问题才是。不必因为“这里看起来需要个模式”而强行套用。很多时候,一个简单的函数或字典就足够了。
  • 优先用语言特性消解模式:如果高阶函数、装饰器、上下文管理器、迭代器能自然表达,就用它们,避免引入多余的类层次。
  • 能读懂的才是好模式:Python 社区更看重明确、直接。一个利用过多魔法方法、元类、动态修改的“优雅”实现,如果让团队成员花半小时才能看懂,那可能还不如一段朴素的代码。
  • 参考标准库和主流框架:它们蕴含了大量成熟的模式运用示例。比如 sort 函数的 key 参数(策略),contextlib.contextmanager 装饰器(模板/上下文),socketserver 里对请求处理的可替换类(模板/策略)。模仿这些习惯写法,你的代码也会更具 Pythonic 味道。

总之,设计模式在 Python 中的最佳状态不是消失,而是融化在语言特性里。你需要记住模式背后的意图,然后用 Python 最自然的方式表达它,而不是去还原另一门语言的代码结构。