单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。
适用性
单例模式的应用一般会有以下条件:
- 资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如日志文件,应用配置。
- 控制资源的情况下,方便资源之间的互相通信。如线程池等。
应用场景
列举几个实际中的应用场景:
- 网站的计数器,一般也是采用单例模式实现,否则难以同步。
- 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
- Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。
起步
在实现单例模式之前,先介绍使用到的知识点
1. __new__
__init__方法通常用在初始化一个类实例的时候。但是__init__其实并不是实例化一个类的时候第一个被调用的方法,最先被调用的方法其实是__new__方法,__init__是在类实例创建之后调用,而__new__方法正是创建这个类实例的方法。
- 继承自object的新式类才有__new__
- __new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供
- __new__必须要有返回值,即返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例
- __init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值
- 若__new__没有正确返回当前类cls的实例,那__init__是不会被调用的,即使是父类的实例也不行
2. __dict__
python中__dict__存储了该对象的一些属性。类和实例分别拥有自己的__dict__,且实例会共享类的__dict__。
3. 装饰器
参考我另一篇博文Python闭包和装饰器
实现
1. 重写__new__方法
通过重写__new__方法继承自该类的类只能创建一个实例
class Singleton(object):
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
temp = super(Singleton, cls)
cls._instance = temp.__new__(cls, *args, **kwargs)
return cls._instance
class MyClass(Singleton):
p = '单例类'
2. 使用装饰器
实现装饰器之后,对类进行装饰即可实现一个单例类
def singleton(cls, *args, **kwargs)
instances = {}
def get_instance():
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instance[cls]
return get_instance
@singleton
class MyClass:
p = '单例类'
3. 使用__dict__共享属性
通过将所有实例的__dict__设置为同一个,即将实例的dict设为类变量。
class Singleton(object):
_state = {}
def __new__(cls, *args, **kwargs):
_instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
_instance.__dict__ = cls._state
return _instance
class MyClass(Singleton):
p = '单例类'
使用这种方式实现单例模式,每次都会创建出新的实例,但是这些实例操作的是同样的数据。
结语
以下代码测试各方式实现的单例模式中创建的实例是否为同一个:
if __name__ == '__main__':
e1 = MyClass()
e2 = MyClass()
if e1 is e2:
print(1)
else:
print(0)
经过测试,使用__new__方法和装饰器版本的是同一个实例,而使用__dict__则是不同的实例
另外,据说通过使用import引入一个创建好的实例,是python中天然的单例模式