理论
单例模式的特点是确保类只有一个实例化对象被创建,并让程序可以全局访问这个对象。
常用于日志记录、数据库操作、打印机后台处理程序等,这些程序在运行过程中只能生成一个实例,以避免对同一资源产生相互冲突的请求。
单例模式是一种经过时间考验的成熟方法,能够在不带来太多缺陷的情况下提供全局访问点。
实战
1、利用python实现经典的单例模式
class Singleton:
def __new__(cls):
if not hasattr(cls, 'instance'):
cls.instance = super(Singleton, cls).__new__(cls)
return cls.instance
a = Singleton()
a2 = Singleton()
print(a, a2)
# 运行结果
# <__main__.Singleton object at 0x109dbd750> <__main__.Singleton object at 0x109dbd750>
使用类名创建对象时,python的解释器首先调用__new__方法为对象分配空间,__new__方法是由object基类提供的内置静态方法,主要的作用有两个,一是在内存中为对象分配空间,二是返回对象的引用。
方法hasattr用于查看对象cls是否具有属性'instance'
2、懒汉实例化
在导入模块的时候,我们可能会无意中创建一个对象,但程序根本没用到它。
懒汉实例化的意思就是可以确保在实际需要的时候才创建对象,是一种节约资源的方式。
class Singleton:
_instance = None
def __init__(self):
if not Singleton._instance:
print('instance未被创建')
else:
print('instance已经被创建:', self.getInstance())
@classmethod
def getInstance(cls):
if not cls._instance:
cls._instance = Singleton()
return cls._instance
s = Singleton() # 实例化对象,但instance并未被创建
Singleton.getInstance() # 创建对象
s1 = Singleton() # 实例化对象, instance已被创建
# 运行结果
'''
instance未被创建
instance未被创建
instance已经被创建: <__main__.Singleton object at 0x103c7f750>
'''
3、模块级别的单例模式
所有的模块都默认为单例,这是由python的导入行为决定的。
在导入一个模块时,python的工作方式为:
- 检查这个模块是否已经被导入。
- 如果已经导入,则返回这个模块的对象,如果没有导入,就导入这个模块,并实例化。
模块在导入的时候就会被初始化,同一个模块再次被导入的时候,它不会再初始化,因为单例模式只有一个对象,它会返回同一个对象。
4、Monostate单例模式
上面我们所说的单例模式指的是GoF(the Gang of Four, GoF)的单例设计模式,核心思想是:一个类有且只有一个对象。
但有另一种说法,Alex Martelli认为通常程序员需要的是让实例共享相同的状态,他建议开发人员应该关注状态和行为,而不是同一性。这个概念关注的是所有对象共享相同状态,因此被称为Monostate(单态)模式。
Monostate模式可以通过python轻松实现。
class Borg:
__shared_state = {'1': '2'}
def __init__(self):
self.x = 1
self.__dict__ = self.__shared_state
b = Borg()
b1 = Borg()
b.x = 4
print(b)
print(b1)
print(b.__dict__)
print(b1.__dict__)
# 运行结果
'''
<__main__.Borg object at 0x10d0a5e50>
<__main__.Borg object at 0x10d1756d0>
{'1': '2', 'x': 4}
{'1': '2', 'x': 4}
'''
python使用__dict__存储一个实例化对象中所有变量的状态。
在上面代码中,在类初始化时我们把类属性__shared_state赋值给__dict__,经过这一步骤,Borg的所有实例化对象的__dict__都将指向__shared_state,也就是说Borg所有实例化对象的__dict__变量成为了同一个变量。
当对象b的实例属性x发生变化,这个变化会反映到对象b的__dict__属性上,由于所有实例化对象的__dict__都指向同一个类属性__shared_state,实例属性__dict__发生变化等同于类属性__shared_state发生变化,这将导致所有实例化对象的__dict__属性发生变化。因此,在实例化对象b的属性x发生变化时,另一个实例化对象b1的实例属性x也会随着发生变化。