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