单例模式
1、什么是单例模式
单例模式指的是类实例化时,只产生单个实例化对象。
实例化多个对象会产生不同的内存地址,单例可以让所有调用者,在调用类产生对象的情况下都指向同一份内存地址。
例如:打开文件。
单例的目的:为了减少内存的占用。
2、产生单例模式的四种方法
2.1 方法一:__new__
借助类内部的__instance
属性,将object.__new__()
产生的对象赋值给它。
如果cls._instance
为None说明该类还没有实例化过,实例化该类,并返回。
如果cls._instance
不为None,直接返回cls._instance
这样就可以在实例化类时,保证使用的都是同一个对象。
class Singleton:
__instance = None
def __new__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = object.__new__(cls)
return cls.__instance
def __init__(self):
self.a = 123
s1 = Singleton()
print(s1,id(s1))
s2 = Singleton()
print(s2,id(s2))
"""
<__main__.Singleton object at 0x000001DA1999F9E8> 2036244019688
<__main__.Singleton object at 0x000001DA1999F9E8> 2036244019688
"""
2.2 方法二:metaclass
通过metaclass属性指定元类,控制类的实例化对象的创建。
元类指定单例类实例化时产生的对象为元类中的__instance
属性对应的对象。
这样就可以使单例类每次实例化都为单例。
class Base(type):
__instance = None
def __call__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = super(Base, cls).__call__(*args, **kwargs)
return cls.__instance
class Singleton(metaclass=Base):
pass
s1 = Singleton()
s2 = Singleton()
print(s1,id(s1))
print(s2,id(s2))
"""
<__main__.Singleton object at 0x0000021AB8B9BC50> 2313791585360
<__main__.Singleton object at 0x0000021AB8B9BC50> 2313791585360
"""
2.3 方法三:装饰器
这也是方法1的升级(高级)版本。
这是一种更pythonic,更elegant的方法,单例类本身根本不知道自己是单例的,因为他本身(自己的代码)并不是单例的。
而装饰器使单例类每次都会返回一个单例对象罢了。
# 普通装饰器
def make_singleton(cls,*args,**kwargs):
_instances = {}
def singleton_class():
if cls not in _instances:
_instances[cls] = cls(*args,**kwargs)
return _instances[cls]
return singleton_class
@make_singleton
class Test:
a = 1
s1 = Test()
s2 = Test()
print(s1,id(s1))
print(s2,id(s2))
'''
<__main__.Test object at 0x0000027515C614E0> 2701899732192
<__main__.Test object at 0x0000027515C614E0> 2701899732192
'''
# 第二种装饰器(强行用lambda函数)
def lambda_decorator(cls,*args,**kwargs):
_instance = cls(*args,**kwargs)
cls.__call__ = lambda _instance: _instance
return _instance
@lambda_decorator
class Test:
a = 1
s1 = Test()
s2 = Test()
print(s1,id(s1))
print(s2,id(s2))
'''
<__main__.Test object at 0x000002318C7F1550> 2411833791824
<__main__.Test object at 0x000002318C7F1550> 2411833791824
'''
2.4 方法四:共享属性
所谓单例就是所有引用(实例、对象)拥有相同的状态(属性)和行为(方法)。
而恰好同一个类的所有实例天然拥有相同的行为(方法),所以只需要保证同一个类的所有实例具有相同的状态(属性)即可。
所有实例共享属性的最简单最直接的方法就是让__dict__
属性指向(引用)同一个字典(dict),这样就可以让实例之间共享属性了。
这样做虽然对象并非真正的同一个对象(id不同),但是由于所有的对象属性与方法都相同,但也可以称作单例。
class Base:
_dic = {}
def __new__(cls, *args, **kwargs):
obj = super().__new__(cls,*args, **kwargs)
obj.__dict__ = cls._dic
return obj
class Singleton(Base):
pass
s1 = Singleton()
s1.name = "single"
s2 = Singleton()
print(s1,s1.__dict__)
print(s2,s2.__dict__)
'''
<__main__.Singleton object at 0x000001C462BD1550> {'name': 'single'}
<__main__.Singleton object at 0x000001C462BCAF60> {'name': 'single'}
'''