写在前面
- 设计模式最初是被 GoF 于 1995 年提出的,GoF(Gang of Four,四人帮)即 Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides。他们四人于 1995 年出版了一本书《Design Patterns:Elements of Reusable Object-Oriented Software》(翻译成中文是《设计模式 可复用面向对象软件的基础》),第一次将设计模式提升到理论高度,并将之规范化,该书提出了 23 种经典的设计模式。
- 市面上关于设计模式的资料大部分都是 Java、C++的案例,我自己学习时,主要写python,边学边写,这个坑也不知道什么时候填完
- Python版本:3.8.10
- 学习来源:https://learn.lianglianglee.com/
- 按照主流的顺序来写,先从创建型模式开始
- 单例模式
- 工厂方法模式
- 抽象工厂模式
- 建造者模式
- 原型模式
单例模式
-
单例模式是一种创建型设计模式,它确保一个类仅有一个实例,并提供一个全局访问点来访问该实例
-
通常有两种写法,懒汉式和饿汉式
- 懒汉式单例模式在类被加载时不会立即创建实例,而是在首次需要使用该实例时才进行创建。这种方式也称为“延迟加载”或“懒加载”。
- 饿汉式单例模式在类被加载时就会立即创建实例。这种方式在类加载时就完成了实例化,因此是线程安全的,无需额外的同步机制。
-
吐槽下,这玩意面试经常问到,有次面试就让我手写线程安全的懒汉式
饿汉式单例模式
- python不太好写饿汉式,不符合Python的惯用做法,我让GPT写了几次都会报错,大致意思到了就行
- 饿汉式单例模式会在类定义时就创建实例
_instance
里面存放对象
class SingleTest:
_instance = None
# 在类定义时直接创建实例
_instance = SingleTest()
def __new__(cls):
# 返回已经创建的实例
return cls._instance
# 使用
singleton_instance = SingleTest()
懒汉式单例模式
- 在使用时才会创建,我在代码中还使用了
_isFirstInit
类变量来确保__init__
方法只被调用一次 __new__
是我们通过类名进行实例化对象时自动调用的,__init__
是在每一次实例化对象之后调用的,__new__
方法创建一个实例之后返回这个实例对象,并将其传递给__init__
方法的 self 参数。- 这段代码有一个潜在的问题:它不是线程安全的。如果在多线程环境中,两个线程同时调用
__new__
方法并发现_instance
为None
,那么它们都可能创建一个新的实例,从而违反单例模式的规则。
class SingletonPattern:
_instance = None
_isFirstInit = False
def __new__(cls, name):
if not cls._instance:
cls._instance = super(SingletonPattern, cls).__new__(cls)
return cls._instance
def __init__(self, name):
if not self._isFirstInit:
self._name = name
print(name)
SingletonPattern._isFirstInit = True
else:
print(name)
def show_name(self):
print(self._name)
if __name__ == '__main__':
single_a_instance = SingletonPattern("jack")
single_a_instance.show_name()
single_b_instance = SingletonPattern("tom")
single_b_instance.show_name()
print(single_a_instance is single_b_instance)
# 输出依次为:
# jack
# jack
# tom
# jack
# True
- 我让GPT改进了下上面的代码,使其在多线程中更安全
- 大致的思路是使用线程锁(如
threading.Lock
)来确保在多线程环境中的原子性
import threading
class SingletonPattern:
_instance = None
_isFirstInit = False
_lock = threading.Lock() # 添加锁
def __new__(cls, name):
with cls._lock: # 锁定
if not cls._instance:
cls._instance = super(SingletonPattern, cls).__new__(cls)
return cls._instance
def __init__(self, name):
with SingletonPattern._lock: # 锁定__init__
if not self._isFirstInit:
self._name = name
SingletonPattern._isFirstInit = True
def show_name(self):
print(self._name)
def create_singleton(name):
instance = SingletonPattern(name)
instance.show_name()
if __name__ == '__main__':
# 创建并运行多线程测试
if __name__ == "__main__":
threads = []
# 创建多个线程,每个线程尝试创建单例
for i in range(10):
t = threading.Thread(target=create_singleton, args=(f"Thread-{i}",))
threads.append(t)
t.start()
# 等待所有线程执行完毕
for t in threads:
t.join()
# 输出都是Thread-0
- 其实还可以将单例的初始化和状态管理完全封装在
__new__
方法中,避免在__init__
方法中进行任何与单例状态相关的逻辑,类似这样
import threading
class SingletonPattern:
_instance = None
_lock = threading.Lock()
def __new__(cls, name=None):
with cls._lock:
if cls._instance is None:
cls._instance = super(SingletonPattern, cls).__new__(cls)
cls._instance._name = name # 直接在__new__中初始化状态
if name:
print(name)
return cls._instance
# 不再需要__init__方法,因为所有初始化都在__new__中完成了
def show_name(self):
print(self._name)
使用metaclass 的方法
class Singleton(type):
"""单例实现方式二"""
def __init__(cls, what, bases=None, dict=None):
print("Singleton.__init__")
super().__init__(what, bases, dict)
cls._instance = None # 初始化全局变量cls._instance为None
def __call__(cls, *args, **kwargs):
print("__call__")
# 控制对象的创建过程,如果cls._instance为None则创建,否则直接返回
if cls._instance is None:
cls._instance = super().__call__(*args, **kwargs)
return cls._instance
class CustomClass(metaclass=Singleton):
"""用户自定义的类"""
def __init__(self, name):
print("CustomClass.__init__")
self.__name = name
def getName(self):
return self.__name
tony = CustomClass("Tony")
print("-------")
karry = CustomClass("Karry")
print(tony.getName(), karry.getName())
print(tony == karry)
# 输出依次为
# Singleton.__init__
# __call__
# CustomClass.__init__
# -------
# __call__
# Tony Tony
# True
metaclass
在用于控制类的实例化过程,代码中CustomClass
使用了Singleton
作为元类,不在这里说太多metaclass
的知识点了- 不难发现,第二次
CustomClass
只输出了__call__
,原因是Singleton.__init__
只在类CustomClass
被定义时调用一次,而不是在每次实例化时调用。 - 第一次调用
CustomClass("Tony")
时- Python 会调用
Singleton.__init__
方法,并输出"Singleton.__init__"
。 - Python 会调用
Singleton.__call__
方法,在__call__
方法中,检查cls._instance
是否为None
。因为这是第一次调用,所以_instance
确实是None
,因此会执行super().__call__(*args, **kwargs)
创建实例。 - 接着会调用
CustomClass.__init__
,初始化实例属性,并输出"CustomClass.__init__"
。
- Python 会调用
- 第二次调用
CustomClass("Karry")
时- 再次调用
Singleton.__call__
方法。 - 此时,
cls._instance
已经不再是None
(因为第一次调用时已经创建了实例并赋值给了_instance
),所以__call__
方法直接返回cls._instance
。 - 由于
cls._instance
已经存在,所以不会再次执行super().__call__(*args, **kwargs)
,也不会再次调用CustomClass.__init__
。
- 再次调用
装饰器的方法
from functools import wraps
def singletonDecorator(func):
"""定义一个单例装饰器"""
instance = {}
@wraps(func)
def wrapperSingleton(*args, **kwargs):
if func not in instance:
instance[func] = func(*args, **kwargs)
print(instance)
return instance[func]
return wrapperSingleton
@singletonDecorator
class Singleton:
"""使用单例装饰器修饰一个类"""
def __init__(self, name):
self.__name = name
def getName(self):
return self.__name
tony = Singleton("Tony")
print("----------")
karry = Singleton("Karry")
print(tony.getName(), karry.getName())
print(tony == karry)
# 输出依次为:
# {<class '__main__.Singleton'>: <__main__.Singleton object at 0x000001A3AD5B08B0>}
# ----------
# {<class '__main__.Singleton'>: <__main__.Singleton object at 0x000001A3AD5B08B0>}
# Tony Tony
# True