综述
单例模式是一种很常见的设计模式,一个类只允许创建一个对象(或者实例),那这个类就是一个单例类
__new__方法
通常我们把__init__称作构造方法,但实际构造实例用的是__new__方法,这是一个特殊的类方法,不需要使用@classmethod装饰器,必须返回一个实例,返回的实例会作为第一个参数(即self)传给__init__方法,而__init__方法调用时需要传入实例,且禁止任何返回值,因此__init__方法其实是初始化方法,真正的构造方法实际是__new__
__new__方法也可以返回其它类的实例,这样就不会调用__init__方法
1 | class A(object): |
可以看到,这里的__new__方法会调用父类__new__方法,实际返回还是一个本身的实例,所以会正常调用__init__方法,结果如下
1 | new |
如果__new__方法不返回本身的实例,则不会去调用自身的__init__方法
1 | class A(object): |
结果可以看到,未调用__init__方法,同时实例化后返回的结果是字符串a
1 | new |
使用__new__方法实现单例
我们可以修改__new__方法,增加一个类属性
1 | class Singleton(object): |
尝试调用
1 | a = MyClass('b') |
结果
1 | a b 4467388368 |
可以看到,当初始化实例b后,实例a的属性aaa也变成了和实例b一样的c,同时实例a和实例b指向同一个地址
其中在调用原始__new__
方法时,可以用注释中的代码,也就是
1 | orig = super(Singleton, cls) |
具体super方法的用法暂时不讨论
使用共享属性实现单例
实例的属性和方法都在__dict__中,只要将所有实例的__dict__方法指向同一个字典,这样他们就具有了相同的属性和方法
1 | class Borg(object): |
尝试调用
1 | a = BorgClass('b') |
结果
1 | a b {'aaa': 'b'} 4467362560 4467142232 |
可以看到,当初始化实例b后,实例a的属性aaa也变成了和实例b一样的c,同时,我们也可以看到,实例a和实例b并不是同一个实例,但是他们的__dict__指向是同一个地址
使用装饰器实现单例
使用装饰器时,可以设置一个字典,根据cls来取对应的实例,这种方法不需要重写__new__方法
1 | def singleton(cls): |
尝试调用
1 | a = DecoraterClass('b') |
结果
1 | a b 4467306224 |
可以看到,当初始化实例b后,实例a的属性aaa也变成了和实例b一样的c,同时,实例a和实例b指向也是同一个地址
使用import方法支持单例
在Python中,模块是天然的单例模式
在文件import_singleton.py中定义
1 | # import_singleton.py |
在另一个文件中引用
1 | from import_singleton import import_singleton_instance |
这样在多个模块里import得到的都是同一个对象
需要注意的是,如果原模块这样写,
1 | # import_singleton.py |
这样在其他模块中初始化得到的并不是同一个对象