2.1 理解单例设计模式
单例模式确保类有且只有一个特定类型的对象,并提供全局访问点。程序运行过程中只能生成一个实例,以免对同一资源产生相互冲突的请求。
单例设计模式的意图如下:
- 确保类有且只有一个对象被创建
- 为对象提供一个访问点,使程序可以全局访问该对象
- 控制共享资源的并行访问
实现单例模式的一个简单方法是,使构造函数私有化,并创建一个静态方法来完成对象的初始化。这样,对象将在第一次调用时创建,此后,类将返回同一对象。
参考如下例子:
class Singleton:
def __new__(cls):
if not hasattr(cls,'instance'):
cls.instance=super(Singleton,cls).__new__(cls)
return cls.instance
s=Singleton()
print('Object created',s)
s1=Singleton()
print('Object created',s1)
输出如下:
Object created <__main__.Singleton object at 0x000002CFC01AB470>
Object created <__main__.Singleton object at 0x000002CFC01AB470>
实例化对象时,调用__init__()初始化之前,先调用了__new__()方法。
__new__()必须要有返回值,返回实例化出来的实例,需要注意的是,可以return父类__new__()出来的实例,也可以直接将object的__new__()出来的实例返回。
__init__()有一个参数self,该self参数就是__new__()返回的实例,__init__()在__new__()的基础上可以完成一些其它初始化的动作,__init__()不需要返回值。
若__new__()没有正确返回当前类cls的实例,那__init__()将不会被调用,即使是父类的实例也不行。
上述代码中,通过覆盖__new__方法来控制对象的创建。在创建之前,此方法会检查对象是否已经存在。
hasattr()用于了解对象是否具有某个属性。instance属性的作用是检查该类是否已经生成了一个对象。
2.2 单例模式中的懒汉实例化
懒汉实例化能够确保在实际需要时才创建对象。
参看如下代码:
class Singleton:
__instance=None
def __init__(self):
if not Singleton.__instance:
print('__init__ method called..')
else:
print('Instance already created:',self.getInstance())
@classmethod
def getInstance(cls):
if not cls.__instance:
cls.__instance=Singleton()
return cls.__instance
s=Singleton()
print('Object created',Singleton.getInstance())
s1=Singleton()
输出如下:
__init__ method called..
__init__ method called..
Object created <__main__.Singleton object at 0x0000020EE04306D8>
Instance already created: <__main__.Singleton object at 0x0000020EE04306D8>
创建s时只实现了类的初始化,但是没有创建实例。
2.3 模块级别的单例模式
默认情况下,所有的模块都是单例。
Python通过下列方式来工作
1.检查一个Python模块是否已经导入
2.如果已经导入,则返回该模块的对象。如果还没有导入,则导入该模块并实例化
3.因此,当模块被导入时,它就会被初始化,然而,当同一个模块被再次导入时,它不会再次初始化。
2.4 Monostate单例模式
通常程序员需要的是让实例共享相同的状态。
参考如下示例:
class Borg:
__shared_state={'1':'2'}
def __init__(self):
self.x=1
self.__dict__=self.__shared_state
pass
b=Borg()
b1=Borg()
b.x=4
print('Borg Object "b": ',b)
print('Borg Object "b1": ',b1)
print('Object State "b":',b.__dict__)
print('Object State "b1":',b1.__dict__)
输出如下:
Borg Object "b": <__main__.Borg object at 0x000002086A8292E8>
Borg Object "b1": <__main__.Borg object at 0x000002086A9CCBE0>
Object State "b": {'1': '2', 'x': 4}
Object State "b1": {'1': '2', 'x': 4}
Python使用__dict__存储一个类所有对象的状态。 类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类的__dict__里;对象的__dict__中存储了一些self.xxx的一些东西。
故虽然b和b1是两个不同的对象,但它们的状态却是相同的。即使对象b的对象变量x发生了变化,这个变化也会复制到被所有对象共享的__dict__变量中。
2.5 单例和元类
元类是一个类的类。类的定义由它的元类决定,故当用类A创建一个类时,Python通过A=type(name,bases,dict)创建它。
- name:类名
- base:这是基类
- dict:这是属性变量
参考如下示例:
class MyInt(type):
def __call__(cls, *args, **kwargs):
print("***** Here's My int *****",args)
print("Now do whatever you want with these objects...")
return type.__call__(cls,*args,**kwargs)
class int(metaclass=MyInt):
def __init__(self,x,y):
self.x=x
self.y=y
i=int(4,5)
输出如下:
***** Here's My int ***** (4, 5)
Now do whatever you want with these objects...
对于已经存在的类来说,当需要创建对象时,将调用Python的特殊方法__call__。在这段代码中,当我们使用int(4,5)实例化int类时,MyInt元类的__call__方法将被调用,这意味着现在元类控制着对象的实例化。
前面的思路同样适用于单例设计模式。由于元类对类创建和对象实例化有更多的控制权,所以它可以用于创建单例。参考如下示例:
class MetaSingleton(type):
_instance={}
def __call__(cls, *args, **kwargs):
if cls not in cls._instance:
cls._instance[cls]=super(MetaSingleton,cls).__call__(*args,**kwargs)
return cls._instance[cls]
class Logger(metaclass=MetaSingleton):
pass
logger1=Logger()
logger2=Logger()
print(logger1)
print(logger2)
输出如下:
<__main__.Logger object at 0x00000214AE58B908>
<__main__.Logger object at 0x00000214AE58B908>
2.6 单例模式例子
需要为基础设施提供运行状况监控服务。创建HelthCheck类,作为单例实现。还要维护一个被监控的服务器列表。当一个服务器从这个列表中删除时,监控软件应该察觉到这一情况,并从被监控的服务器列表中将其删除。
当添加或删除服务器时,运行状况的检查工作必须由了解基础设施变动情况的同一个对象来完成。
class HealthCheck:
_instance=None
def __new__(cls, *args, **kwargs):
if not HealthCheck._instance:
HealthCheck._instance=super(HealthCheck,cls).__new__(cls,*args,**kwargs)
return HealthCheck._instance
def __init__(self):
self._servers=[]
def addSever(self):
self._servers.append("Sever 1")
self._servers.append("Sever 2")
self._servers.append("Sever 3")
self._servers.append("Sever 4")
def changeSever(self):
self._servers.pop()
self._servers.append("Sever 5")
hc1=HealthCheck()
hc2=HealthCheck()
hc1.addSever()
print('Schedule health check for servers (1)..')
for i in range(4):
print('Checking ',hc1._servers[i])
hc2.changeSever()
print('Schedule health check for servers (2)..')
for i in range(4):
print('Checking ',hc2._servers[i])
输出如下:
Schedule health check for servers (1)..
Checking Sever 1
Checking Sever 2
Checking Sever 3
Checking Sever 4
Schedule health check for servers (2)..
Checking Sever 1
Checking Sever 2
Checking Sever 3
Checking Sever 5
2.7 单例模式的缺点
- 全局变量可能在某处已经被误改,但是开发人员仍然认为它们没有发生变化,而该变量还在应用程序的其他位置被使用。
- 可能会对同一对象创建多个引用。由于单例只创建一个对象,因此这种情况下会对同一个对象创建多个引用。
- 所有依赖于全局变量的类都会由于一个类的改变而紧密耦合为全局数据,从而可能在无意中影响另一个类。
在许多实际应用程序中,只需要创建一个对象,如线程池、缓存、对话框、注册表设置等。如果我们为每个应用程序创建多个实例,则会导致资源的过渡使用。单例模式在这种情况下工作得很好。