Chapter3 Creational Design Pattern
1、单例模式的定义、用途、实现方法
单例模式(Singleton Pattern)是一种创建型设计模式,其核心目标是确保一个类只有一个实例,并为该实例提供全局访问点。在 Python 中,它通常用于管理全局唯一的资源(如配置、日志、数据库连接池等)。
核心概念
-
唯一性
无论创建多少次对象,实际只会生成第一个实例,后续调用返回同一个实例。 -
全局访问
通过类方法或模块级变量提供统一的访问入口,避免重复创建。
Python 实现方式
方法 1:使用模块(Pythonic 方式)
Python 的模块天然支持单例模式,因为模块在程序中只会被加载一次。
# singleton.py class Singleton: def __init__(self): self.data = "I'm the only instance!" _instance = Singleton() def get_instance(): return _instance # 使用方式 from singleton import get_instance obj = get_instance()
方法 2:重写 __new__ 方法
通过控制实例化过程,确保只创建一个对象。
class Singleton: _instance = None # 类变量保存唯一实例 def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super().__new__(cls) return cls._instance # 测试 a = Singleton() b = Singleton() print(a is b) # 输出 True
方法 3:使用装饰器
通过装饰器包装类,控制实例的创建。
def singleton(cls): instances = {} def wrapper(*args, **kwargs): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return wrapper @singleton class Logger: def __init__(self): self.log_level = "DEBUG" # 测试 logger1 = Logger() logger2 = Logger() print(logger1 is logger2) # 输出 True
方法 4:使用元类(Metaclass)
通过元类控制类的创建过程。
class SingletonMeta(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super().__call__(*args, **kwargs) return cls._instances[cls] class Database(metaclass=SingletonMeta): def __init__(self): self.connection = "Connected" # 测试 db1 = Database() db2 = Database() print(db1 is db2) # 输出 True
难点理解, cls._instances[cls] 这种写法的理解?
cls在__call__方法中,表示的是元类Singleton的类对象,但它会被用来操作当前类(DataBase)。cls._instance[cls]中的两个cls,实际上是不同的:- 第一个
cls是元类Singleton,它操作的是元类的实例(即DataBase)。 - 第二个
cls代表的是当前类(在调用DataBase()时,指的就是DataBase类本身),它作为字典的键,存储对应类的单例实例。
- 第一个
这就是这两个 cls 在 cls._instance[cls] 中的区别和作用。
在元类的 __call__ 方法中,第一个参数 cls 代表的是当前调用元类的类,而不是实例。具体来说:
- 这个
cls是在执行DataBase()时传入的,而DataBase使用的是Singleton元类。所以,cls在这里实际上是Singleton元类,即元类本身,而不是DataBase类。
应用场景
-
配置管理:全局唯一的配置对象。
-
日志记录器:统一管理日志写入。
-
数据库连接池:避免重复创建连接。
-
缓存系统:共享缓存数据。
注意事项
-
线程安全
多线程环境下需加锁(如使用threading.Lock)确保原子操作。 -
可测试性
单例的全局状态可能增加测试复杂度,可通过依赖注入优化。 -
过度使用问题
单例会引入全局耦合,仅在必要时使用。
单例模式通过控制实例化过程,确保资源的高效利用和一致性,是 Python 中管理全局资源的常用模式。
2, 设计模式中,工厂模式和抽象工厂模式的概念和区别,python代码分别举例说明
工厂模式和抽象工厂模式都是创建型设计模式,主要用于对象的创建。让我们分别看看它们:
- 工厂模式(Factory Pattern)
- 工厂模式提供了一个单一的工厂类,根据传入的参数决定创建哪个产品类的实例
- 一个工厂类负责创建一个产品等级结构中的产品对象
让我们看一个简单的动物工厂示例:
# 产品基类 class Animal: def speak(self): pass # 具体产品 class Dog(Animal): def speak(self): return "Woof!" class Cat(Animal): def speak(self): return "Meow!" # 工厂类 class AnimalFactory: def create_animal(self, animal_type): if animal_type == "dog": return Dog() elif animal_type == "cat": return Cat() else: raise ValueError("Unknown animal type") # 使用示例 factory = AnimalFactory() dog = factory.create_animal("dog") print(dog.speak()) # 输出: Woof! cat = factory.create_animal("cat") print(cat.speak()) # 输出: Meow!
抽象工厂模式(Abstract Factory Pattern)
- 抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口
- 无需指定它们的具体类,可以创建多个产品等级结构中的产品对象
让我们用一个家具制造的例子来说明:
from abc import ABC, abstractmethod # 抽象产品 class Chair(ABC): @abstractmethod def sit_on(self): pass class Table(ABC): @abstractmethod def put_on(self): pass # 具体产品 - 现代风格 class ModernChair(Chair): def sit_on(self): return "Sitting on a modern chair" class ModernTable(Table): def put_on(self): return "Putting on a modern table" # 具体产品 - 古典风格 class ClassicChair(Chair): def sit_on(self): return "Sitting on a classic chair" class ClassicTable(Table): def put_on(self): return "Putting on a classic table" # 抽象工厂 class FurnitureFactory(ABC): @abstractmethod def create_chair(self): pass @abstractmethod def create_table(self): pass # 具体工厂 class ModernFurnitureFactory(FurnitureFactory): def create_chair(self): return ModernChair() def create_table(self): return ModernTable() class ClassicFurnitureFactory(FurnitureFactory): def create_chair(self): return ClassicChair() def create_table(self): return ClassicTable() # 客户端代码 def create_furniture_set(factory: FurnitureFactory): chair = factory.create_chair() table = factory.create_table() return chair, table # 使用示例 modern_factory = ModernFurnitureFactory() classic_factory = ClassicFurnitureFactory() # 创建现代家具 modern_chair, modern_table = create_furniture_set(modern_factory) print(modern_chair.sit_on()) # 输出: Sitting on a modern chair print(modern_table.put_on()) # 输出: Putting on a modern table # 创建古典家具 classic_chair, classic_table = create_furniture_set(classic_factory) print(classic_chair.sit_on()) # 输出: Sitting on a classic chair print(classic_table.put_on()) # 输出: Putting on a classic table
主要区别:
- 抽象层次不同:
- 工厂模式只有一个抽象层(产品的抽象)
- 抽象工厂模式有两个抽象层(产品的抽象和工厂的抽象)
- 创建对象的范围不同:
- 工厂模式创建一个产品等级结构中的一个产品
- 抽象工厂模式创建多个产品等级结构中的产品
- 使用场景不同:
- 工厂模式适用于产品种类相对较少的情况
- 抽象工厂模式适用于有多个产品系列,每个系列包含多个产品的情况
- 扩展性不同:
- 工厂模式增加新产品需要修改工厂类
- 抽象工厂模式增加新的产品系列只需要增加新的工厂类,不需要修改原有代码
3、设计模式中的单例模式, 该如何理解,举例说明并总结特点。
如下是单例模式的特点,我将用 Python 代码来演示。
class DatabaseConnection: _instance = None def __new__(cls): if cls._instance is None: print("创建新的数据库连接实例") cls._instance = super().__new__(cls) # 初始化数据库连接 cls._instance.initialize() return cls._instance def initialize(self): self.connected = False self.connection_string = "mongodb://localhost:27017" def connect(self): if not self.connected: print(f"连接到数据库: {self.connection_string}") self.connected = True else: print("已经连接到数据库") def disconnect(self): if self.connected: print("断开数据库连接") self.connected = False else: print("当前没有活跃的数据库连接") # 测试代码 def test_singleton(): # 创建第一个实例 db1 = DatabaseConnection() db1.connect() # 创建第二个实例 db2 = DatabaseConnection() db2.connect() # 验证是否是同一个实例 print("\n验证单例:") print(f"db1 的 id: {id(db1)}") print(f"db2 的 id: {id(db2)}") print(f"db1 和 db2 是否是同一个实例: {db1 is db2}") # 测试断开连接 db1.disconnect() db2.disconnect() # 实际上是同一个实例 if __name__ == "__main__": test_singleton()
返回结果:
创建新的数据库连接实例 连接到数据库: mongodb://localhost:27017 已经连接到数据库 验证单例: db1 的 id: 2407900395952 db2 的 id: 2407900395952 db1 和 db2 是否是同一个实例: True 断开数据库连接 当前没有活跃的数据库连接
单例模式的核心特点和使用场景:
- 核心特点:
- 确保一个类只有一个实例,并提供一个全局访问点
- 实例在首次使用时才会被创建(懒加载)
- 所有后续获取实例的请求都返回同一个实例
- 构造函数通常是私有的,防止直接创建新实例
- 适用场景:
- 数据库连接管理:避免创建多个数据库连接实例
- 配置管理器:整个应用程序共享同一份配置信息
- 日志记录器:集中管理日志记录
- 打印机后台处理程序:控制打印任务队列
- 文件系统:提供统一的文件访问接口
- 优点:
- 节省系统资源,避免重复创建对象
- 保证系统中对象的唯一性
- 提供了对唯一实例的受控访问
- 注意事项:
- 在多线程环境下需要特别注意线程安全
- 单例模式可能会使代码的测试变得较为困难
- 过度使用单例可能导致代码耦合度过高
3、追问:cls._instance = super().new(cls) 该如何进行理解?
这行代码涉及到 Python 类的实例化过程,我们需要理解几个关键点:
__new__方法的作用:- 这是 Python 类实例化过程中的第一步
- 它在
__init__之前被调用 - 负责创建并返回实例对象
- 是一个静态方法,第一个参数通常命名为 cls,代表类本身
super().__new__(cls)的含义:super()获取父类(这里是 object 类)的引用- 调用父类的
__new__方法 【父类为什么有这个方法,看接下来追问】 - 传入 cls 参数,表示要为哪个类创建实例
- 这会创建一个全新的对象实例
让我们通过一个更简单的例子来说明:
class Regular: def __new__(cls): print("1. __new__ 被调用") instance = super().__new__(cls) # 创建实例 print("2. 实例被创建") return instance def __init__(self): print("3. __init__ 被调用") self.value = 42 # 创建实例 obj = Regular() print("4. 实例的值:", obj.value)
输出会是:
1. __new__ 被调用 2. 实例被创建 3. __init__ 被调用 4. 实例的值: 42
4、追问:为什么父类有这个 __new__ 方法,可以直接拿来调用?
Python 中的对象系统设计得很巧妙。让我来解释这个问题:
- 所有的 Python 类都继承自
object类:
# 这两种写法是等价的 class MyClass: # Python 3 中默认继承 object pass class MyClass(object): # 显式继承 object pass
2. object 类实现了一些特殊方法(魔术方法),__new__ 就是其中之一:
>>> dir(object) # 查看 object 类的所有属性和方法 ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
3. __new__ 方法是对象实例化的底层实现:
# object.__new__ 的工作原理大致是: def __new__(cls, *args, **kwargs): # 1. 分配内存空间 # 2. 创建一个原始的实例对象 # 3. 返回这个实例 return new_instance
让我们通过一个例子来看实例创建的完整过程:
class Example: def __new__(cls, *args, **kwargs): print("1. __new__ 被调用") print("2. 通过 super().__new__ 创建实例") instance = super().__new__(cls) print("3. 实例类型:", type(instance)) return instance def __init__(self): print("4. __init__ 被调用") print("5. 开始初始化实例") # 创建实例 obj = Example()
1. __new__ 被调用 2. 通过 super().__new__ 创建实例 3. 实例类型: <class '__main__.Example'> 4. __init__ 被调用 5. 开始初始化实例
这个过程说明:
- 当你调用
Example()时:- 首先调用
__new__方法 __new__必须返回一个实例对象- 如果返回的是
Example类的实例,则会继续调用__init__
- 首先调用
super().__new__(cls)的作用:- 调用父类(object)的
__new__方法 - 创建一个原始的实例对象
- 这个实例还没有被初始化(没有调用
__init__)
- 调用父类(object)的
- 为什么需要
__new__:- 它允许我们控制实例的创建过程
- 可以在对象创建前进行一些操作
- 可以改变返回的实例类型
- 是实现单例模式等特殊模式的关键
__new__vs__init__:__new__负责创建实例__init__负责初始化实例__new__必须返回一个实例__init__只能返回 None
理解了这些,你就知道为什么我们可以调用 super().__new__ 了 —— 因为它是 Python 对象系统的基础部分,提供了创建对象实例的底层机制。每个类都从 object 继承了这个能力,这就是面向对象中继承的强大之处。
5、为什么这么懒,非得要调用父类(object)的 __new__ 方法去创建实例?自己难道不能去创建实例吗
这是一个很好的问题!让我们深入理解为什么需要调用父类的 __new__ 方法。
- 实例创建的底层操作:
- 创建一个实例需要在内存中分配空间
- 需要设置正确的类型信息
- 需要建立正确的继承关系
- 需要初始化所有底层的数据结构
这些都是非常底层的操作,在 Python 中是由 C 代码实现的。我们来看一个尝试自己创建实例的例子:
这会失败,因为:
object()创建的是一个基础对象- 我们无法正确设置它的类型和属性
- 它缺少了必要的内部结构
再看一个更极端的例子:
class MyClass: def __new__(cls): # 尝试用字典模拟一个实例 instance = { '__class__': cls, 'say_hello': lambda: print("Hello!") } return instance # 测试 obj = MyClass() print(type(obj)) # 输出: <class 'dict'> # obj.say_hello() # 这会失败,因为obj是字典而不是真正的MyClass实例
这说明:
- 自己创建实例是非常复杂的
- 需要处理很多底层的细节
- 可能会破坏 Python 的对象模型
正确的方式是使用 super().__new__(cls):
class MyClass: def __new__(cls): # 让 object.__new__ 处理实例创建的复杂细节 instance = super().__new__(cls) return instance def say_hello(self): print("Hello!") # 测试 obj = MyClass() print(type(obj)) # 输出: <class '__main__.MyClass'> obj.say_hello() # 正常工作
这样做的好处是:
- 利用了 Python 内置的实例创建机制
- 确保实例有正确的类型信息
- 保持了对象模型的完整性
- 支持继承和方法解析顺序(MRO)
总结:
- 调用父类的
__new__是最安全和最标准的创建实例的方式 - 它处理了所有复杂的底层细节
- 确保实例被正确创建和初始化
- 维护了 Python 的对象系统的完整性
这就像是在建造房子:你可以自己从头开始建造每一个部分,但使用现成的地基(object.__new__)会更安全可靠,因为它已经处理好了所有基础性的工作。

浙公网安备 33010602011771号