定义
他提供了一个机制:确保一个类只有一个实例对象。
例如日志记录,数据库操作,打印机后台处理程序等。
实现
最简单的实现方法是使构造函数私有化,并创建一个静态方法来完成对象的初始化。然后对象将在第一次调用时创建,之后这个类将返回同一个对象。
注意:
- 类只创建一个对象
- 为对象提供一个访问点,使程序可以全局访问该对象
- 控制共享资源的并行访问
饿汉式单例
当类初始化的时候,就创建这个实例对象,以后永远返回同一个实例对象。
class Singleton1(object):
# 通过覆盖__new__方法来控制对象的创建。
def __new__(cls, *args, **kwargs):
# hasattr用于查看对象cls是否有instance属性,该属性作用是检测该类是否已经生成了一个对象
if not hasattr(cls, 'instance'):
cls.instance = super(Singleton1, cls).__new__(cls)
return cls.instance
s = Singleton1()
s1 = Singleton1()
print(s)
print(s1)
'''结果:
<__main__.Singleton1 object at 0x102c3b198>
<__main__.Singleton1 object at 0x102c3b198>'''
懒汉式单例
初始化类的时候不创建对象,第一次调用才创建。这个时候就要注意线程安全性了。
Python内置有一个全局锁会保证只进入一个线程调用类的方法,所以没有这个问题,但如果其他语言实现就要考虑这个问题,比如java就应该用synchronized锁住代码块确保线程安全。这一问题也是造成Python的多线程不是真正的多线程的原因。
class Singleton2(object):
__instance = None
# 初始化时,如果存在对象,就直接返回这个对象,不存在就不管,也不new它
def __init__(self):
if Singleton2.__instance:
self.get_instance()
# 实际的对象创建发生在调用get_instance的时候
@classmethod
def get_instance(cls):
if not cls.__instance:
cls.__instance = Singleton2()
return cls.__instance
s = Singleton2()
print(s.get_instance())
s1 = Singleton2()
print(s1.get_instance())
'''结果:
<__main__.Singleton2 object at 0x110062240>
<__main__.Singleton2 object at 0x110062240>'''
使用元类实现单例
元类MetaSingleton生成的实例是Logger类,关于__call__方法的调用,因为Logger是MetaSingleton的一个实例,所以Logger()实际上就调用了Singleton的__call__方法,所以在这一层面上控制了Logger对象的单例。
如果不能理解,看下面模块:
1、python类里的__call__()魔法方法能够让类的实例对象像函数一样被调用。意思就是:A类创建对象a:
a = new A()
,a是一个对象,不能调用,但如果A类有call方法,他就能通过a()
执行A类的call方法。
2、Python一切皆对象,包括你写的类,你写的Python文件等。所以既然是对象,就有他的类。类的类就叫元类,元类也有类,元类的类是type类,type是Python的内建元类。
class MetaSingleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(MetaSingleton,
cls).__call__(*args, **kwargs)
return cls._instances
class Logger(metaclass=MetaSingleton):
pass
logger1 = Logger()
logger2 = Logger()
print(logger1)
print(logger2)
'''结果:
{<class '__main__.Logger'>: <__main__.Logger object at 0x10357be10>}
{<class '__main__.Logger'>: <__main__.Logger object at 0x10357be10>}'''
单例模式优缺点
- 单例模式优点很明显:只提供一个实例化的对象。
- 单例模式具有全局访问权限,全局变量可能在某处已经被修改,但是开发人员仍然认为他们没有发生变化。
- 可能会对同一个对象创建多个引用。
- 所有依赖于全局变量的类都会由于一个类的改变而紧密偶合为全局数据,从而可能在无意中影响另一个类。