转自 https://blog.csdn.net/u012324798/article/details/104056562
结论先行
本文在比较了Python实现单例模式8种方法的优缺点之后,认为在Python中使用元类实现单例模式效果最好,推荐使用元类实现单例。
为什么要使用单例模式?
通常情况下,一个类可以多次实例化,每个实例对象是相互独立的,即类在同一时刻可以有多种实例状态。
如果我们希望:
- 某个类在在同一时刻不能有多种实例状态,即【单态】(例如:操作系统的任务管理器、回收站、文件系统;应用程序的日志应用、网站的计时工具或ID(序号)生成器);
- 或者某个类只需要一个实例就够用了,即【单例】,多的实例不会有什么坏的影响,但也没什么作用,只是徒增资源消耗而已(例如:数据库连接池、线程池、web开发中读取配置文件)。
这时如果某个类在程序运行期间只有一个实例,就能实现上述需求(单例 / 单态)。
最简单的办法就是只实例化一次得到一个实例,自己记住,之后写代码的时候一直用它,不再实例化。问题是自己有可能忘了某个类已经实例化过了,而且别人想复用你的代码时也不知道你已经实例化过了,就很容易发生重复实例化,自然无法确保单例效果。为了解决这个问题,我们希望在这个类本身能够实现无论实例化多少次,每次返回的都是同一个实例的效果,这就是单例模式。这样后续使用这个类的人就无需关注是否已经实例化过,反正重复实例化得到的还是单例。
实例化过程原理
类():即obj = MyClass() ,执行MyClass的元类的__call__方法,如果没有使用metaclass指定元类,则默认元类为type
实例():即obj(),执行MyClass类的__call__方法
obj = MyClass() 这一实例化过程其实是调用其元类的__call__方法,如果未指定元类,则调用默认元类type的__call__方法,而这一方法是由type_call函数(CPython 中的 C代码)来实现的,其实现的基本逻辑简化后相当于Python的以下代码:即先调用MyClass的__new__方法创建实例,如果实例符合2个条件,则调用MyClass的__init__方法对实例进行初始化。
def __call__(obj_type, *args, **kwargs):
obj = obj_type.__new__(*args, **kwargs)
if obj is not None and isinstance(obj, obj_type):
obj.__init__(*args, **kwargs)
return obj
由此可见,控制实例化过程的是3个特殊方法:
- 元类的__call__方法是实例化过程中最早调用的方法,它控制了类的__new__和__init__方法的调用
- 类的__new__方法是第二个调用的方法,影响创建实例的具体过程
- 类的__init__方法是最后调用的方法,影响实例的初始化
想要让MyClass成为单例模式,在实例化过程前加上条件判断就可以。问题是在哪里增加条件判断的代码?由于条件判断的代码所在位置不同,单例模式有多种实现方式。本文的重点就是比较分析这些实现方式的优缺点。
评价标准
为了比较网上比较流行的Python实现单例模式的8种方法,提出以下评价标准:
- 将单例模式的代码独立封装为Singleton方便复用,MyClass及其子类SubClass无需关心单例模式的实现
- MyClass类有继承的基类OtherClass时不影响单例效果
- 单例模式不影响MyClass作为基类被SubClass继承,且子类SubClass也是单例模式,即SubClass每次实例化得到都是同一个SubClass实例
- 变量MyClass代表的是真正的MyClass类,能使用类属性、类方法
- type(obj) 返回的是真正的MyClass类
- type(obj) () 返回的也是单例,单例效果稳定
- 有 lazy loading的效果,即导入模块时没有发生实例化,不消耗资源,等到需要使用时再实例化
- 多线程并发环境下得到的也是单例
除了实现方式(一)、(二)以外,都使用以下测试代码:
test.py
import threading
import time
from singleton_class import MyClass
def print_task(obj=None, arg=1):
if obj is None:
obj = MyClass()
before = obj.__dict__
print('arg: %d, id(obj): %d' % (arg, id(obj)))
print('arg: %d, before: %s' % (arg, before))
obj.x = 'x_%d' % arg
obj.a = 'a_%d' % arg
if hasattr(obj, 'y'):
obj.y = 'y_%d' % arg
after = obj.__dict__
print('arg: %d, after: %s' % (arg, after))
if __name__ == '__main__':
print('_____________________________MyClass MultiThreading Test______________________________________')
for i in range(1, 11):
t = threading.Thread(target=print_task, args=(None, i))
t.start()
time.sleep(4)
print('_____________________________MyClass______________________________________')
print('MyClass: %s, type(MyClass): %s' % (MyClass, type(MyClass)))
try:
print('MyClass.class_attribute(): %s' % MyClass.class_attribute)
MyClass.class_func()
except AttributeError:
print('由于type(MyClass): %s,变量MyClass不能使用类属性、类方法' % type(MyClass))
obj1 = MyClass()
print_task(obj1, 1)
obj2 = MyClass()
print_task(obj2, 2)
print('type(obj): %s' % type(obj1))
obj3 = type(obj1)()
print_task(obj3, 3)
print('_____________________________SubClass______________________________________')
try:
class SubClass(MyClass):
def __init__(self):
super(SubClass, self).__init__()
self.y = 'y_default'
# # 实现方式(五)、(六)SubClass需改用以下代码
# class SubClass(MyClass):
# def __init__(self):
# # __init__方法需避免重复初始化成员变量
# if not self._initialed:
# super(SubClass, self).__init__()
# self.y = 'y_default'
# self._initialed = True
print('SubClass: %s, type(SubClass): %s' % (SubClass, type(SubClass)))
print('SubClass.class_attribute(): %s' % SubClass.class_attribute)
SubClass.class_func()
sub_obj1 = SubClass()
print_task(sub_obj1, 11)
sub_obj2 = SubClass()
print_task(sub_obj2, 12)
print('type(sub_obj): %s' % type(sub_obj1))
sub_obj3 = type(sub_obj1)()
print_task(sub_obj3, 13)
except TypeError:
print('由于type(MyClass): %s,变量MyClass不能作为基类被继承' % type(MyClass))
(一)使用类方法 getInstance 作为获取实例的接口
此实现方式将条件判断的代码放在了类方法 getInstance 中,必须调用类方法 getInstance 作为获取实例的接口才能获得单例,如果使用 obj = MyClass() 这种常规方式,则获取到的不是单例,非常不实用,就不测试分析了,直接跳过。
class MyClass(object):
_instance = None
def __init__(self):
self.x = 0
@classmethod
def get_instance(cls):
if cls._instance is None:
cls._instance = cls()
return cls._instance
(二)使用模块(同名实例替换类变量)
此实现方式没有增加条件判断的代码,而是借助了模块的单例特性,确保只有一个实例存在。
为什么Python模块就是天然的单例模式?
由于在程序运行期间模块只装载一次,并且模块A中的全局变量a绑定成了模块的属性,即A.a只有一个。如果A.a代表的是实例,并且切断了其他实例化的途径,则该类的实例就只有一个。
在声明 MyClass 类之后直接实例化一个单例保存在同名的全局变量 MyClass 中,即MyClass = MyClass(),这样变量 MyClass 代表的就是一个实例,不是类,也就不能通过 obj = MyClass() 这行代码进行实例化,会报错。为了不让这个这行代码报错,给MyClass类增加一个__call__方法(使得实例可以向函数一样通过 () 被调用),返回实例本身。这样每次通过 obj = MyClass() 这行代码进行“实例化”时,并没有真正进行实例化,而是调用了实例的__call__方法,返回实例本身,从而实现了单例模式。
singleton_class.py
import time
class Singleton(object):
# 给单例类增加一个__call__方法,返回实例本身
def __call__(self, *args, **kwargs):
return self
class OtherClass(object):
def __new__(cls):
return super(OtherClass, cls).__new__(cls)
def __init__(self):
self.a = 'a_default'
super(OtherClass, self).__init__()
class MyClass(Singleton, OtherClass):
class_attribute = 'class_default'
def __init__(self):
time.sleep(1)
# 注意:由于模块装载后,变量 MyClass 代表的是实例,不是类,不能使用super(MyClass, self).__init__()
# 要获得真实的MyClass类,需使用__class__
super(__class__, self).__init__()
self.x = 'x_default'
@classmethod
def class_func(cls):
print('class function')
# 同名实例替换类变量
MyClass = MyClass()
test.py
import threading
import time
def print_task(obj, arg):
before = obj.__dict__
print('arg: %d, id(obj): %d' % (arg, id(obj)))
print('arg: %d, before: %s' % (arg, before))
obj.x = 'x_%d' % arg
obj.a = 'a_%d' % arg
if hasattr(obj, 'y'):
obj.y = 'y_%d' % arg
after = obj.__dict__
print('arg: %d, after: %s' % (arg, after))
def task(arg):
print('arg: %d before import' % arg)
# import语句是线程安全的,即使多线程并发导入同一个模块,也不会重复装载模块
from singleton_class import MyClass
print_task(MyClass(), arg)
if __name__ == '__main__':
print('_____________________________MyClass MultiThreading Test______________________________________')
for i in range(1, 11):
t = threading.Thread(target=task, args=(i, ))
t.start()
time.sleep(2)
from ingleton_class import MyClass
print('_____________________________MyClass______________________________________')
print('type(MyClass): %s' % type(MyClass))
print('MyClass.class_attribute(): %s' % MyClass.class_attribute)
MyClass.class_func()
obj1 = MyClass()
print_task(obj1, 1)
obj2 = MyClass()
print_task(obj2, 2)
obj3 = type(obj1)()
print_task(obj3, 3)
print('_____________________________SubClass______________________________________')
# 注意:变量MyClass代表的是实例,不是类,不能直接用变量MyClass作为基类,而需使用MyClass.__class__作为基类
class SubClass(MyClass.__class__):
def __init__(self):
# 由于模块装载后,变量 SubClass 代表的是实例,不是类,不能使用super(SubClass, self).__init__()
# 要获得真实的SubClass类,需使用__class__
super(__class__, self).__init__()
self.y = 'y_default'
# 同名实例替换类变量
SubClass = SubClass()
print('SubClass: %s, type(SubClass): %s' % (SubClass, type(SubClass)))
print('SubClass.class_attribute(): %s' % SubClass.class_attribute)
SubClass.class_func()
sub_obj1 = SubClass()
print_task(sub_obj1, 11)
sub_obj2 = SubClass()
print_task(sub_obj2, 12)
print('type(sub_obj): %s' % type(sub_obj1))
sub_obj3 = type(sub_obj1)()
print_task(sub_obj3, 13)
输出:
_____________________________ MyClass MultiThreading Test______________________________________
arg: 1 before import
arg: 2 before import
arg: 3 before import
arg: 4 before import
arg: 5 before import
arg: 6 before import
arg: 7 before import
arg: 8 before import
arg: 9 before import
arg: 10 before import
arg: 1, id(obj): 40451880
arg: 1, before: {‘a’: ‘a_default’, ‘x’: ‘x_default’}
arg: 1, after: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, id(obj): 40451880
arg: 2, before: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 3, id(obj): 40451880
arg: 3, before: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 3, after: {‘a’: ‘a_3’, ‘x’: ‘x_3’}
arg: 4, id(obj): 40451880
arg: 2, after: {‘a’: ‘a_2’, ‘x’: ‘x_2’}
arg: 5, id(obj): 40451880
arg: 5, before: {‘a’: ‘a_2’, ‘x’: ‘x_2’}
arg: 5, after: {‘a’: ‘a_5’, ‘x’: ‘x_5’}
arg: 6, id(obj): 40451880
arg: 6, before: {‘a’: ‘a_5’, ‘x’: ‘x_5’}
arg: 6, after: {‘a’: ‘a_6’, ‘x’: ‘x_6’}
arg: 4, before: {‘a’: ‘a_6’, ‘x’: ‘x_6’}
arg: 4, after: {‘a’: ‘a_4’, ‘x’: ‘x_4’}
arg: 7, id(obj): 40451880
arg: 8, id(obj): 40451880
arg: 8, before: {‘a’: ‘a_4’, ‘x’: ‘x_4’}
arg: 7, before: {‘a’: ‘a_4’, ‘x’: ‘x_4’}
arg: 7, after: {‘a’: ‘a_7’, ‘x’: ‘x_7’}
arg: 8, after: {‘a’: ‘a_8’, ‘x’: ‘x_8’}
arg: 9, id(obj): 40451880
arg: 9, before: {‘a’: ‘a_8’, ‘x’: ‘x_8’}
arg: 10, id(obj): 40451880
arg: 10, before: {‘a’: ‘a_8’, ‘x’: ‘x_8’}
arg: 10, after: {‘a’: ‘a_10’, ‘x’: ‘x_10’}
arg: 9, after: {‘a’: ‘a_9’, ‘x’: ‘x_9’}
_____________________________ MyClass______________________________________
MyClass: <singleton_class.MyClass object at 0x0000000002693F28>, type(MyClass): <class ‘singleton_class.MyClass’>
MyClass.class_attribute(): class_default
class function
arg: 1, id(obj): 40451880
arg: 1, before: {‘a’: ‘a_9’, ‘x’: ‘x_9’}
arg: 1, after: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, id(obj): 40451880
arg: 2, before: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, after: {‘a’: ‘a_2’, ‘x’: ‘x_2’}
type(obj): <class ‘singleton_class.MyClass’>
arg: 3, id(obj): 40101928
arg: 3, before: {‘a’: ‘a_default’, ‘x’: ‘x_default’}
arg: 3, after: {‘a’: ‘a_3’, ‘x’: ‘x_3’}
_____________________________ SubClass______________________________________
SubClass: <__ main__.SubClass object at 0x00000000026752E8>, type(SubClass): <class ‘__ main__.SubClass’>
SubClass.class_attribute(): class_default
class function
arg: 11, id(obj): 40325864
arg: 11, before: {‘a’: ‘a_default’, ‘x’: ‘x_default’, ‘y’: ‘y_default’}
arg: 11, after: {‘a’: ‘a_11’, ‘x’: ‘x_11’, ‘y’: ‘y_11’}
arg: 12, id(obj): 40325864
arg: 12, before: {‘a’: ‘a_11’, ‘x’: ‘x_11’, ‘y’: ‘y_11’}
arg: 12, after: {‘a’: ‘a_12’, ‘x’: ‘x_12’, ‘y’: ‘y_12’}
type(sub_obj): <class ‘__ main__.SubClass’>
arg: 13, id(obj): 40326032
arg: 13, before: {‘a’: ‘a_default’, ‘x’: ‘x_default’, ‘y’: ‘y_default’}
arg: 13, after: {‘a’: ‘a_13’, ‘x’: ‘x_13’, ‘y’: ‘y_13’}
优点:
- (核心优点)无需设置双重校验锁,自动实现在多线程并发环境下仍然是单例,原因是实例化发生在模块装载时,而import语句是线程安全的,即使多线程并发导入同一个模块,也不会重复装载模块,从而确保了只有一个实例存在
- MyClass类有继承的基类OtherClass时不影响单例效果
- 变量MyClass代表的是MyClass类的实例,能使用类属性、类方法
- type(obj) 返回的是真正的MyClass类
缺点:
- 单例模式的代码不能完全独立封装,MyClass类及其子类SubClass为了实现单例模式,必须注意使用同名实例替换类变量
- 单例模式影响MyClass作为基类被SubClass继承,因为变量MyClass代表的是实例,不是类,不能直接用变量MyClass作为基类,而需使用MyClass.__class__作为基类
- 通过obj3 = type(obj1)() 得到的实例 obj3 不是单例,单例效果不稳定
- 没有达到lazy loading的效果,在装载模块时就完成实例化了
(三)使用函数装饰器
此实现方式将MyClass这个变量替换为函数装饰器,从而可以在装饰器中进行条件判断。由于装饰器只是替换了MyClass这个变量,真正的实例化过程并未开始,当符合条件判断时,只需通过 obj = MyClass() 这种常规方式完成实例化即可。
singleton_class.py
import threading
import time
# 写法1
def Singleton(cls):
_instance = dict()
_instance_lock = threading.Lock()
def _wrapper(*args, **kargs):
# 外层校验是为了避免单例已产生后,线程还要拿锁,浪费锁资源
if cls not in _instance:
with _instance_lock:
# 内层校验是单例逻辑的条件判断,必须放在锁内,是为了避免线程在等锁的过程中单例已产生
if cls not in _instance:
_instance[cls] = cls(*args, **kargs)
return _instance[cls]
return _wrapper
# # 写法2
# def Singleton(cls):
# _instance = None
# _instance_lock = threading.Lock()
#
# def _wrapper(*args, **kargs):
# nonlocal _instance
# # 外层校验是为了避免单例已产生后,线程还要拿锁,浪费锁资源
# if _instance is None:
# with _instance_lock:
# # 内层校验是单例逻辑的条件判断,必须放在锁内,是为了避免线程在等锁的过程中单例已产生
# if _instance is None:
# _instance = cls(*args, **kargs)
# return _instance
#
# return _wrapper
class OtherClass(object):
def __new__(cls):
return super(OtherClass, cls).__new__(cls)
def __init__(self):
self.a = 'a_default'
super(OtherClass, self).__init__()
@Singleton
class MyClass(OtherClass):
class_attribute = 'class_default'
def __init__(self):
time.sleep(1)
# 注意:经过装饰后的变量 MyClass 代表的是函数,不是类,不能使用super(MyClass, self).__init__()
# 要获得真实的MyClass类,需使用__class__
super(__class__, self).__init__()
self.x = 'x_default'
@classmethod
def class_func(cls):
print('class function')
输出:
_____________________________ MyClass MultiThreading Test______________________________________
arg: 1, id(obj): 39810552
arg: 2, id(obj): 39810552
arg: 2, before: {‘a’: ‘a_default’, ‘x’: ‘x_default’}
arg: 2, after: {‘a’: ‘a_2’, ‘x’: ‘x_2’}
arg: 1, before: {‘a’: ‘a_2’, ‘x’: ‘x_2’}
arg: 1, after: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 3, id(obj): 39810552
arg: 4, id(obj): 39810552
arg: 3, before: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 3, after: {‘a’: ‘a_3’, ‘x’: ‘x_3’}
arg: 5, id(obj): 39810552
arg: 5, before: {‘a’: ‘a_3’, ‘x’: ‘x_3’}
arg: 5, after: {‘a’: ‘a_5’, ‘x’: ‘x_5’}
arg: 4, before: {‘a’: ‘a_5’, ‘x’: ‘x_5’}
arg: 6, id(obj): 39810552
arg: 7, id(obj): 39810552
arg: 7, before: {‘a’: ‘a_5’, ‘x’: ‘x_5’}
arg: 6, before: {‘a’: ‘a_5’, ‘x’: ‘x_5’}
arg: 6, after: {‘a’: ‘a_6’, ‘x’: ‘x_6’}
arg: 7, after: {‘a’: ‘a_7’, ‘x’: ‘x_7’}
arg: 4, after: {‘a’: ‘a_4’, ‘x’: ‘x_4’}
arg: 8, id(obj): 39810552
arg: 8, before: {‘a’: ‘a_4’, ‘x’: ‘x_4’}
arg: 8, after: {‘a’: ‘a_8’, ‘x’: ‘x_8’}
arg: 9, id(obj): 39810552
arg: 9, before: {‘a’: ‘a_8’, ‘x’: ‘x_8’}
arg: 9, after: {‘a’: ‘a_9’, ‘x’: ‘x_9’}
arg: 10, id(obj): 39810552
arg: 10, before: {‘a’: ‘a_9’, ‘x’: ‘x_9’}
arg: 10, after: {‘a’: ‘a_10’, ‘x’: ‘x_10’}
_____________________________ MyClass______________________________________
MyClass: <function Singleton.< locals >._ wrapper at 0x00000000025EEEA0>, type(MyClass): <class ‘function’>
由于type(MyClass): <class ‘function’>,变量MyClass不能使用类属性、类方法
arg: 1, id(obj): 39810552
arg: 1, before: {‘a’: ‘a_10’, ‘x’: ‘x_10’}
arg: 1, after: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, id(obj): 39810552
arg: 2, before: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, after: {‘a’: ‘a_2’, ‘x’: ‘x_2’}
type(obj): <class ‘singleton_class.MyClass’>
arg: 3, id(obj): 39727056
arg: 3, before: {‘a’: ‘a_default’, ‘x’: ‘x_default’}
arg: 3, after: {‘a’: ‘a_3’, ‘x’: ‘x_3’}
_____________________________ SubClass______________________________________
由于type(MyClass): <class ‘function’>,变量MyClass不能作为基类被继承
优点:
- 将单例模式的代码独立封装,MyClass类无需关心单例模式的实现
- MyClass有继承的基类OtherClass时不影响单例效果
- type(obj) 返回的是真正的MyClass类
- 有 lazy loading 效果
缺点:
- 单例模式导致MyClass不能作为基类被SubClass继承
- 经过装饰后的变量MyClass代表的是一个_wrapper函数,而不是真正的MyClass类,不能使用MyClass的类属性、类方法
- obj3 = type(obj1)() 得到的实例 obj3 不是单例,单例效果不稳定
- 需手动设置双重校验锁,才能在保证多线程并发环境下得到的也是单例
(四)使用类装饰器
此实现方式与(三)使用函数装饰器非常相似,没有本质区别,只不过装饰器的写法不同。
singleton_class.py
import threading
import time
class Singleton(object):
_instance_lock = threading.Lock()
def __init__(self, cls):
self._cls = cls
self._instance = None
def __call__(self, *args, **kwargs):
if self._instance is None:
with Singleton._instance_lock:
if self._instance is None:
self._instance = self._cls(*args, **kwargs)
return self._instance
class OtherClass(object):
def __new__(cls):
return super(OtherClass, cls).__new__(cls)
def __init__(self):
self.a = 'a_default'
super(OtherClass, self).__init__()
@Singleton
class MyClass(OtherClass):
class_attribute = 'class_default'
def __init__(self):
time.sleep(1)
# 注意:经过装饰后的变量 MyClass 代表的是实例,不是类,不能使用super(MyClass, self).__init__()
# 要获得真实的MyClass类,需使用__class__
super(__class__, self).__init__()
self.x = 'x_default'
@classmethod
def class_func(cls):
print('class function')
输出:
_____________________________ MyClass MultiThreading Test______________________________________
arg: 1, id(obj): 39923272
arg: 2, id(obj): 39923272
arg: 2, before: {‘a’: ‘a_default’, ‘x’: ‘x_default’}
arg: 2, after: {‘a’: ‘a_2’, ‘x’: ‘x_2’}
arg: 1, before: {‘a’: ‘a_2’, ‘x’: ‘x_2’}
arg: 1, after: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 3, id(obj): 39923272
arg: 3, before: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 3, after: {‘a’: ‘a_3’, ‘x’: ‘x_3’}
arg: 4, id(obj): 39923272
arg: 4, before: {‘a’: ‘a_3’, ‘x’: ‘x_3’}
arg: 4, after: {‘a’: ‘a_4’, ‘x’: ‘x_4’}
arg: 5, id(obj): 39923272
arg: 5, before: {‘a’: ‘a_4’, ‘x’: ‘x_4’}
arg: 5, after: {‘a’: ‘a_5’, ‘x’: ‘x_5’}
arg: 6, id(obj): 39923272
arg: 6, before: {‘a’: ‘a_5’, ‘x’: ‘x_5’}
arg: 6, after: {‘a’: ‘a_6’, ‘x’: ‘x_6’}
arg: 7, id(obj): 39923272
arg: 7, before: {‘a’: ‘a_6’, ‘x’: ‘x_6’}
arg: 8, id(obj): 39923272
arg: 7, after: {‘a’: ‘a_7’, ‘x’: ‘x_7’}
arg: 9, id(obj): 39923272
arg: 8, before: {‘a’: ‘a_7’, ‘x’: ‘x_7’}
arg: 8, after: {‘a’: ‘a_8’, ‘x’: ‘x_8’}
arg: 10, id(obj): 39923272
arg: 9, before: {‘a’: ‘a_8’, ‘x’: ‘x_8’}
arg: 9, after: {‘a’: ‘a_9’, ‘x’: ‘x_9’}
arg: 10, before: {‘a’: ‘a_9’, ‘x’: ‘x_9’}
arg: 10, after: {‘a’: ‘a_10’, ‘x’: ‘x_10’}
_____________________________ MyClass______________________________________
MyClass: <singleton_class.Singleton object at 0x00000000026260B8>, type(MyClass): <class ‘singleton_class.Singleton’>
由于type(MyClass): <class ‘singleton_class.Singleton’>,变量MyClass不能使用类属性、类方法
arg: 1, id(obj): 39923272
arg: 1, before: {‘a’: ‘a_10’, ‘x’: ‘x_10’}
arg: 1, after: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, id(obj): 39923272
arg: 2, before: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, after: {‘a’: ‘a_2’, ‘x’: ‘x_2’}
type(obj): <class ‘singleton_class.MyClass’>
arg: 3, id(obj): 39744456
arg: 3, before: {‘a’: ‘a_default’, ‘x’: ‘x_default’}
arg: 3, after: {‘a’: ‘a_3’, ‘x’: ‘x_3’}
_____________________________ SubClass______________________________________
由于type(MyClass): <class ‘singleton_class.Singleton’>,变量MyClass不能作为基类被继承
优缺点与函数装饰器相同,区别在于经过装饰后的变量MyClass代表的是Singleton类(装饰器)的实例,它也不代表真正的MyClass类。
(五)使用装饰器返回类
在实现方式(三)、(四)中,经过装饰后的变量MyClass代表的都不是类,不能作为基类被SubClass继承,为了解决这个问题,考虑使用装饰器返回类的实现方式。
由实例化过程原理分析,我们知道了:类的__new__方法是第二个调用的方法,影响创建实例的具体过程;类的__init__方法是最后调用的方法,影响实例的初始化。
那么,通过在类的__new__方法增加条件判断的代码,就能实现单例模式。但是没这么简单,必须要注意三个问题:
- 每次调用__new__方法之后,会紧接着调用__init__方法。所以,必须给出一个标志,避免__init__方法重复调用导致成员变量被默认值覆盖。
- 多线程并发环境下,如果MyClass类的__init__方法中有耗时操作,则装饰器内的__init__方法也要加双重校验锁。
- 经过装饰后的变量MyClass代表的是class_wrapper类,从而SubClass继承的就是class_wrapper类,但SubClass真正想要继承的是MyClass类。于是,为了能够让SubClass继承到MyClass类,class_wrapper类必须继承于MyClass类。
singleton_class.py
import threading
import time
def Singleton(_cls):
# 为了能够让SubClass继承到MyClass类,class_wrapper类必须继承于MyClass类
class class_wrapper(_cls):
# 由于class_wrapper的类属性_instance被所有单例类共享,为保证各单例类之间相互独立,_instance需使用可变数据类型(例如:字典)
_instance = dict()
_instance_lock = threading.Lock()
def __new__(cls):
if cls not in class_wrapper._instance.keys():
with class_wrapper._instance_lock:
if cls not in class_wrapper._instance.keys():
class_wrapper._instance[cls] = super(class_wrapper, cls).__new__(cls)
# 需给出一个标志,从而__init__方法可以根据此标志避免重复初始化成员变量
class_wrapper._instance[cls]._intialed = False
return class_wrapper._instance[cls]
def __init__(self):
# __init__方法需避免重复初始化成员变量,并且加双重校验锁
if not self._intialed:
with class_wrapper._instance_lock:
if not self._intialed:
super(class_wrapper, self).__init__()
self._intialed = True
class_wrapper.__name__ = _cls.__name__
return class_wrapper
class OtherClass(object):
def __new__(cls):
return super(OtherClass, cls).__new__(cls)
def __init__(self):
self.a = 'a_default'
super(OtherClass, self).__init__()
@Singleton
class MyClass(OtherClass):
class_attribute = 'class_default'
def __init__(self):
time.sleep(1)
# 注意:经过装饰后的变量 MyClass 代表的是class_wrapper类,而不是真实的MyClass类,不能使用super(MyClass, self).__init__()
# 要获得真实的MyClass类,需使用__class__
super(__class__, self).__init__()
self.x = 'x_default'
@classmethod
def class_func(cls):
print('class function')
注意: test.py中的SubClass需改为以下代码
# 实现方式(五)、(六)SubClass需改用以下代码
class SubClass(MyClass):
def __init__(self):
# __init__方法需避免重复初始化成员变量
if not self._initialed:
super(SubClass, self).__init__()
self.y = 'y_default'
self._initialed = True
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
输出:
_____________________________ MyClass MultiThreading Test______________________________________
arg: 1, id(obj): 39289072
arg: 1, before: {’_ intialed’: True, ‘a’: ‘a_default’, ‘x’: ‘x_default’}
arg: 1, after: {’_ intialed’: True, ‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, id(obj): 39289072
arg: 2, before: {’_ intialed’: True, ‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, after: {’_ intialed’: True, ‘a’: ‘a_2’, ‘x’: ‘x_2’}
arg: 3, id(obj): 39289072
arg: 3, before: {’_ intialed’: True, ‘a’: ‘a_2’, ‘x’: ‘x_2’}
arg: 3, after: {’_ intialed’: True, ‘a’: ‘a_3’, ‘x’: ‘x_3’}
arg: 4, id(obj): 39289072
arg: 4, before: {’_ intialed’: True, ‘a’: ‘a_3’, ‘x’: ‘x_3’}
arg: 4, after: {’_ intialed’: True, ‘a’: ‘a_4’, ‘x’: ‘x_4’}
arg: 5, id(obj): 39289072
arg: 5, before: {’_ intialed’: True, ‘a’: ‘a_4’, ‘x’: ‘x_4’}
arg: 5, after: {’_ intialed’: True, ‘a’: ‘a_5’, ‘x’: ‘x_5’}
arg: 6, id(obj): 39289072
arg: 6, before: {’_ intialed’: True, ‘a’: ‘a_5’, ‘x’: ‘x_5’}
arg: 6, after: {’_ intialed’: True, ‘a’: ‘a_6’, ‘x’: ‘x_6’}
arg: 7, id(obj): 39289072
arg: 7, before: {’_ intialed’: True, ‘a’: ‘a_6’, ‘x’: ‘x_6’}
arg: 7, after: {’_ intialed’: True, ‘a’: ‘a_7’, ‘x’: ‘x_7’}
arg: 8, id(obj): 39289072
arg: 8, before: {’_ intialed’: True, ‘a’: ‘a_7’, ‘x’: ‘x_7’}
arg: 8, after: {’_ intialed’: True, ‘a’: ‘a_8’, ‘x’: ‘x_8’}
arg: 9, id(obj): 39289072
arg: 9, before: {’_ intialed’: True, ‘a’: ‘a_8’, ‘x’: ‘x_8’}
arg: 9, after: {’_ intialed’: True, ‘a’: ‘a_9’, ‘x’: ‘x_9’}
arg: 10, id(obj): 39289072
arg: 10, before: {’_ intialed’: True, ‘a’: ‘a_9’, ‘x’: ‘x_9’}
arg: 10, after: {’_ intialed’: True, ‘a’: ‘a_10’, ‘x’: ‘x_10’}
_____________________________ MyClass______________________________________
MyClass: <class ‘singleton_class.Singleton.< locals >.class_wrapper’>, type(MyClass): <class ‘type’>
MyClass.class_attribute(): class_default
class function
arg: 1, id(obj): 39289072
arg: 1, before: {’_ intialed’: True, ‘a’: ‘a_10’, ‘x’: ‘x_10’}
arg: 1, after: {’_ intialed’: True, ‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, id(obj): 39289072
arg: 2, before: {’_ intialed’: True, ‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, after: {’_ intialed’: True, ‘a’: ‘a_2’, ‘x’: ‘x_2’}
type(obj): <class ‘singleton_class.Singleton.< locals >.class_wrapper’>
arg: 3, id(obj): 39289072
arg: 3, before: {’_ intialed’: True, ‘a’: ‘a_2’, ‘x’: ‘x_2’}
arg: 3, after: {’_ intialed’: True, ‘a’: ‘a_3’, ‘x’: ‘x_3’}
_____________________________ SubClass______________________________________
SubClass: <class ‘__ main__.SubClass’>, type(SubClass): <class ‘type’>
SubClass.class_attribute(): class_default
class function
arg: 11, id(obj): 39187120
arg: 11, before: {’_ initialed’: True, ‘a’: ‘a_default’, ‘x’: ‘x_default’, ‘y’: ‘y_default’}
arg: 11, after: {’_ initialed’: True, ‘a’: ‘a_11’, ‘x’: ‘x_11’, ‘y’: ‘y_11’}
arg: 12, id(obj): 39187120
arg: 12, before: {’_ initialed’: True, ‘a’: ‘a_11’, ‘x’: ‘x_11’, ‘y’: ‘y_11’}
arg: 12, after: {’_ initialed’: True, ‘a’: ‘a_12’, ‘x’: ‘x_12’, ‘y’: ‘y_12’}
type(sub_obj): <class ‘__ main__.SubClass’>
arg: 13, id(obj): 39187120
arg: 13, before: {’_ initialed’: True, ‘a’: ‘a_12’, ‘x’: ‘x_12’, ‘y’: ‘y_12’}
arg: 13, after: {’_ initialed’: True, ‘a’: ‘a_13’, ‘x’: ‘x_13’, ‘y’: ‘y_13’}
优点:
- MyClass类无需关心单例模式的实现
- MyClass有继承的基类OtherClass时不影响单例效果
- 单例模式不影响MyClass作为基类被继承,且子类SubClass也是单例模式,即SubClass每次实例化得到都是同一个SubClass实例
- 经过装饰后的变量MyClass代表了class_wrapper类,它是MyClass类的子类,从而能使用MyClass类的类属性、类方法
- 通过obj3 = type(obj1)() 得到的实例也是单例,单例效果稳定
- 有 lazy loading 效果
缺点:
- 单例模式的代码不能完全独立封装,子类SubClass需关心单例模式的实现(必须注意不能重载__new__方法,否则SubClass不是单例模式;__init__方法需注意避免重复初始化成员变量)
- type(obj) 返回的是class_wrapper类,不是真正的MyClass类,即obj实际上是class_wrapper类的单例
- 需手动设置双重校验锁,才能在保证多线程并发环境下得到的也是单例
(六)使用基类
在实现方式(五)中,经过装饰后的变量MyClass代表了class_wrapper类,不是真正的MyClass类。为了解决这个问题,考虑使用基类的实现方式。
这种实现方式也是通过在类的__new__方法增加条件判断的代码。但是Singleton是基类,MyClass是子类,基类无法控制子孙类方法的调用,因此必须要注意三个问题:
- 每次调用__new__方法之后,会紧接着调用__init__方法。所以,必须给出一个标志,避免__init__方法重复调用导致成员变量被默认值覆盖。
- 基类Singleton无法控制子孙类方法的调用,如果子孙类有自己特殊的成员变量需要初始化,则__init__方法不能封装到基类Singleton中。
- 多线程并发环境下,如果__init__方法中有耗时操作,则__init__方法也要加双重校验锁。
singleton_class.py
import threading
import time
class Singleton(object):
# 由于Singleton的类属性_instance被所有单例类共享,为保证各单例类之间相互独立,_instance需使用可变数据类型(例如:字典)
_instance = dict()
_instance_lock = threading.Lock()
def __new__(cls, *args, **kwargs):
if cls not in Singleton._instance.keys():
with Singleton._instance_lock:
if cls not in Singleton._instance.keys():
Singleton._instance[cls] = super(Singleton, cls).__new__(cls)
# 需给出一个标志,从而__init__方法可以根据此标志避免重复初始化成员变量
Singleton._instance[cls]._intialed = False
return Singleton._instance[cls]
class OtherClass(object):
def __new__(cls):
return super(OtherClass, cls).__new__(cls)
def __init__(self):
self.a = 'a_default'
super(OtherClass, self).__init__()
class MyClass(Singleton, OtherClass):
class_attribute = 'class_default'
def __init__(self):
# __init__方法需避免重复初始化成员变量,并且加双重校验锁
if not self._initialed:
with self._instance_lock:
if not self._initialed:
super(MyClass, self).__init__()
time.sleep(1)
self.x = 'x_default'
self._initialed = True
@classmethod
def class_func(cls):
print('class function')
注意: test.py中的SubClass需改为以下代码
# 实现方式(五)、(六)SubClass需改用以下代码
class SubClass(MyClass):
def __init__(self):
# __init__方法需避免重复初始化成员变量
if not self._initialed:
super(SubClass, self).__init__()
self.y = 'y_default'
self._initialed = True
输出:
_____________________________ MyClass MultiThreading Test______________________________________
arg: 1, id(obj): 39579888
arg: 1, before: {’_ intialed’: True, ‘a’: ‘a_default’, ‘x’: ‘x_default’}
arg: 1, after: {’_ intialed’: True, ‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, id(obj): 39579888
arg: 2, before: {’_ intialed’: True, ‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, after: {’_ intialed’: True, ‘a’: ‘a_2’, ‘x’: ‘x_2’}
arg: 3, id(obj): 39579888
arg: 3, before: {’_ intialed’: True, ‘a’: ‘a_2’, ‘x’: ‘x_2’}
arg: 3, after: {’_ intialed’: True, ‘a’: ‘a_3’, ‘x’: ‘x_3’}
arg: 4, id(obj): 39579888
arg: 4, before: {’_ intialed’: True, ‘a’: ‘a_3’, ‘x’: ‘x_3’}
arg: 4, after: {’_ intialed’: True, ‘a’: ‘a_4’, ‘x’: ‘x_4’}
arg: 5, id(obj): 39579888
arg: 5, before: {’_ intialed’: True, ‘a’: ‘a_4’, ‘x’: ‘x_4’}
arg: 5, after: {’_ intialed’: True, ‘a’: ‘a_5’, ‘x’: ‘x_5’}
arg: 6, id(obj): 39579888
arg: 6, before: {’_ intialed’: True, ‘a’: ‘a_5’, ‘x’: ‘x_5’}
arg: 6, after: {’_ intialed’: True, ‘a’: ‘a_6’, ‘x’: ‘x_6’}
arg: 7, id(obj): 39579888
arg: 7, before: {’_ intialed’: True, ‘a’: ‘a_6’, ‘x’: ‘x_6’}
arg: 7, after: {’_ intialed’: True, ‘a’: ‘a_7’, ‘x’: ‘x_7’}
arg: 8, id(obj): 39579888
arg: 8, before: {’_ intialed’: True, ‘a’: ‘a_7’, ‘x’: ‘x_7’}
arg: 9, id(obj): 39579888
arg: 9, before: {’_ intialed’: True, ‘a’: ‘a_7’, ‘x’: ‘x_7’}
arg: 8, after: {’_ intialed’: True, ‘a’: ‘a_8’, ‘x’: ‘x_8’}
arg: 9, after: {’_ intialed’: True, ‘a’: ‘a_9’, ‘x’: ‘x_9’}
arg: 10, id(obj): 39579888
arg: 10, before: {’_ intialed’: True, ‘a’: ‘a_9’, ‘x’: ‘x_9’}
arg: 10, after: {’_ intialed’: True, ‘a’: ‘a_10’, ‘x’: ‘x_10’}
MyClass_________
MyClass: <class ‘singleton_class.MyClass’>, type(MyClass): <class ‘type’>
MyClass.class_attribute(): class_default
class function
arg: 1, id(obj): 39579888
arg: 1, before: {’_ intialed’: True, ‘a’: ‘a_10’, ‘x’: ‘x_10’}
arg: 1, after: {’_ intialed’: True, ‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, id(obj): 39579888
arg: 2, before: {’_ intialed’: True, ‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, after: {’_ intialed’: True, ‘a’: ‘a_2’, ‘x’: ‘x_2’}
type(obj): <class ‘singleton_class.MyClass’>
arg: 3, id(obj): 39579888
arg: 3, before: {’_ intialed’: True, ‘a’: ‘a_2’, ‘x’: ‘x_2’}
arg: 3, after: {’_ intialed’: True, ‘a’: ‘a_3’, ‘x’: ‘x_3’}
_____________________________ SubClass______________________________________
SubClass: <class ‘__ main__.SubClass’>, type(SubClass): <class ‘type’>
SubClass.class_attribute(): class_default
class function
arg: 11, id(obj): 39974392
arg: 11, before: {’_ intialed’: True, ‘a’: ‘a_default’, ‘x’: ‘x_default’, ‘y’: ‘y_default’}
arg: 11, after: {’_ intialed’: True, ‘a’: ‘a_11’, ‘x’: ‘x_11’, ‘y’: ‘y_11’}
arg: 12, id(obj): 39974392
arg: 12, before: {’_ intialed’: True, ‘a’: ‘a_11’, ‘x’: ‘x_11’, ‘y’: ‘y_11’}
arg: 12, after: {‘intialed’: True, ‘a’: ‘a_12’, ‘x’: ‘x_12’, ‘y’: ‘y_12’}
type(sub_obj): <class '_ main__.SubClass’>
arg: 13, id(obj): 39974392
arg: 13, before: {’_ intialed’: True, ‘a’: ‘a_12’, ‘x’: ‘x_12’, ‘y’: ‘y_12’}
arg: 13, after: {’_ intialed’: True, ‘a’: ‘a_13’, ‘x’: ‘x_13’, ‘y’: ‘y_13’}
优点:
- MyClass类有继承的基类OtherClass时不影响单例效果(前提条件:Singleton需作为第一基类)
- 单例模式不影响MyClass类作为基类被继承,且子类SubClass也是单例模式,即SubClass每次实例化得到都是同一个SubClass实例
- 变量MyClass代表的是真正的MyClass类,能使用类属性、类方法
- type(obj) 返回的是真正的MyClass类
- 通过obj3 = type(obj1)() 得到的实例也是单例,单例效果稳定
- 有 lazy loading 效果
缺点:
- 单例模式的代码不能完全独立封装,MyClass类、SubClass类需关心单例模式的实现(必须注意不重载__new__方法,否则不是单例;__init__方法需避免重复初始化成员变量;Singleton需作为第一基类)
- 需手动设置双重校验锁,才能在保证多线程并发环境下得到的也是单例
(七)使用元类
实现方式(五)、(六)都是通过在类的__new__方法中增加条件判断的代码来实现单例模式的,导致单例模式的代码不能完全独立封装。
回顾实例化过程的原理,元类的__call__方法是实例化过程中最早调用的方法,从而可以在元类的__call__方法中增加条件判断。
需要注意的是,由于此时实例化过程已开始,当符合条件判断时,需通过super函数调用父元类type的__call__方法完成实例化。
singleton_class.py
import threading
import time
# 写法1
# 元类Singleton不保存所有单例类的实例,各单例类通过自己的类属性cls._instance保存单例
class Singleton(type):
_instance_lock = threading.Lock()
# 元类的__init__方法负责在类创建后初始化类属性
def __init__(cls, *args, **kwargs):
# 虽然子类SubClass创建时继承了基类MyClass的类属性_instance,但是会在此处被重置为None,从而SubClass也是单例模式
cls._instance = None
super(Singleton, cls).__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs):
# 外层校验是为了避免单例已产生后,线程还要拿锁,浪费锁资源
if cls._instance is None:
with Singleton._instance_lock:
# 内层校验是单例逻辑的条件判断,必须放在锁内,是为了避免线程在等锁的过程中单例已产生
if cls._instance is None:
# 通过super函数调用父元类type的__call__方法完成实例化
cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instance
# # 写法2
# # 元类属性_instances保存了所有单例类的实例,各单例类本身没有保存单例,各单例类也不会继承元类属性
# class Singleton(type):
# _instances = dict()
# _instance_lock = threading.Lock()
#
# def __call__(cls, *args, **kwargs):
# if cls not in cls._instances.keys():
# with Singleton._instance_lock:
# if cls not in cls._instances.keys():
# cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
# return cls._instances[cls]
class OtherClass(object):
def __new__(cls):
return super(OtherClass, cls).__new__(cls)
def __init__(self):
self.a = 'a_default'
super(OtherClass, self).__init__()
class MyClass(OtherClass, metaclass=Singleton):
class_attribute = 'class_default'
def __init__(self):
time.sleep(1)
super(MyClass, self).__init__()
self.x = 'x_default'
@classmethod
def class_func(cls):
print('class function')
输出:
_____________________________ MyClass MultiThreading Test______________________________________
arg: 1, id(obj): 39340128
arg: 1, before: {‘a’: ‘a_default’, ‘x’: ‘x_default’}
arg: 2, id(obj): 39340128
arg: 1, after: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, before: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, after: {‘a’: ‘a_2’, ‘x’: ‘x_2’}
arg: 3, id(obj): 39340128
arg: 3, before: {‘a’: ‘a_2’, ‘x’: ‘x_2’}
arg: 3, after: {‘a’: ‘a_3’, ‘x’: ‘x_3’}
arg: 4, id(obj): 39340128
arg: 4, before: {‘a’: ‘a_3’, ‘x’: ‘x_3’}
arg: 4, after: {‘a’: ‘a_4’, ‘x’: ‘x_4’}
arg: 5, id(obj): 39340128
arg: 5, before: {‘a’: ‘a_4’, ‘x’: ‘x_4’}
arg: 5, after: {‘a’: ‘a_5’, ‘x’: ‘x_5’}
arg: 6, id(obj): 39340128
arg: 6, before: {‘a’: ‘a_5’, ‘x’: ‘x_5’}
arg: 6, after: {‘a’: ‘a_6’, ‘x’: ‘x_6’}
arg: 7, id(obj): 39340128
arg: 7, before: {‘a’: ‘a_6’, ‘x’: ‘x_6’}
arg: 7, after: {‘a’: ‘a_7’, ‘x’: ‘x_7’}
arg: 8, id(obj): 39340128
arg: 8, before: {‘a’: ‘a_7’, ‘x’: ‘x_7’}
arg: 8, after: {‘a’: ‘a_8’, ‘x’: ‘x_8’}
arg: 9, id(obj): 39340128
arg: 9, before: {‘a’: ‘a_8’, ‘x’: ‘x_8’}
arg: 9, after: {‘a’: ‘a_9’, ‘x’: ‘x_9’}
arg: 10, id(obj): 39340128
arg: 10, before: {‘a’: ‘a_9’, ‘x’: ‘x_9’}
arg: 10, after: {‘a’: ‘a_10’, ‘x’: ‘x_10’}
MyClass_________
MyClass: <class ‘singleton_class.MyClass’>, type(MyClass): <class ‘singleton_class.Singleton’>
MyClass.class_attribute(): class_default
class function
arg: 1, id(obj): 39340128
arg: 1, before: {‘a’: ‘a_10’, ‘x’: ‘x_10’}
arg: 1, after: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, id(obj): 39340128
arg: 2, before: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, after: {‘a’: ‘a_2’, ‘x’: ‘x_2’}
type(obj): <class ‘singleton_class.MyClass’>
arg: 3, id(obj): 39340128
arg: 3, before: {‘a’: ‘a_2’, ‘x’: ‘x_2’}
arg: 3, after: {‘a’: ‘a_3’, ‘x’: ‘x_3’}
SubClass_________
SubClass: <class ‘__ main__.SubClass’>, type(SubClass): <class ‘singleton_class.Singleton’>
SubClass.class_attribute(): class_default
class function
arg: 11, id(obj): 39982808
arg: 11, before: {‘a’: ‘a_default’, ‘x’: ‘x_default’, ‘y’: ‘y_default’}
arg: 11, after: {‘a’: ‘a_11’, ‘x’: ‘x_11’, ‘y’: ‘y_11’}
arg: 12, id(obj): 39982808
arg: 12, before: {‘a’: ‘a_11’, ‘x’: ‘x_11’, ‘y’: ‘y_11’}
arg: 12, after: {‘a’: ‘a_12’, ‘x’: ‘x_12’, ‘y’: ‘y_12’}
type(sub_obj): <class ‘__ main__.SubClass’>
arg: 13, id(obj): 39982808
arg: 13, before: {‘a’: ‘a_12’, ‘x’: ‘x_12’, ‘y’: ‘y_12’}
arg: 13, after: {‘a’: ‘a_13’, ‘x’: ‘x_13’, ‘y’: ‘y_13’}
优点:
- 将单例模式的代码独立封装,MyClass及其子类SubClass无需关心单例模式的实现
- MyClass类有继承的基类OtherClass时不影响单例效果
- 单例模式不影响MyClass类作为基类被继承,且子类SubClass也是单例模式,即SubClass每次实例化得到都是同一个SubClass实例
- 变量MyClass代表的是真正的MyClass类,能使用类属性、类方法
- type(obj) 返回的是真正的MyClass类
- 通过obj3 = type(obj1)() 得到的实例也是单例,单例效果稳定
- 有 lazy loading 效果
缺点:
- 需手动设置双重校验锁,才能在保证多线程并发环境下得到的也是单例
(八)Borg单态模式(所有实例共享状态)
上述7种实现方式都是通过确保只有一个实例存在,从而实现单态的,而Borg模式的思路是"实例的唯一性并不是重要的,我们应该关注的是实例的状态,只要所有的实例共享状态,行为一致,那就达到了单例的目的"。通过Borg模式,可以创建任意数量的实例,但因为它们共享状态,从而实现了单态。所以严格来说,Borg模式不是单例模式,而是单态模式。
Borg模式代码的核心是,将所有实例的属性字典都指向同一个内存地址,这样就能实现虽然实例有多个,但属性字典只有一个,从而所有的实例共享状态。
需要注意的是,实例可以有多个,所以__new__方法的调用不限次数;但初始化成员变量只能有一次,所以__init__方法只能调用一次。由于元类的__call__方法能够控制这两个方法的调用,所以采用元类封装Borg模式的代码。
import threading
import time
class Singleton(type):
_instance_lock = threading.Lock()
# 元类的__init__方法负责在类创建后初始化类属性
def __init__(cls, *args, **kwargs):
cls._shared_state = dict()
cls._initialed = False
super(Singleton, cls).__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs):
obj = cls.__new__(cls, *args, **kwargs)
# 将所有实例的属性字典都指向同一个内存地址,虽然实例有多个,但属性字典只有一个,从而实现所有实例共享状态
obj.__dict__ = cls._shared_state
if not cls._initialed and obj is not None and isinstance(obj, cls):
with Singleton._instance_lock:
if not cls._initialed and obj is not None and isinstance(obj, cls):
obj.__init__(*args, **kwargs)
cls._initialed = True
return obj
class OtherClass(object):
def __new__(cls):
return super(OtherClass, cls).__new__(cls)
def __init__(self):
self.a = 'a_default'
super(OtherClass, self).__init__()
class MyClass(OtherClass, metaclass=Singleton):
class_attribute = 'class_attribute'
def __init__(self):
super(MyClass, self).__init__()
time.sleep(1)
self.x = 'x_default'
@classmethod
def class_func(cls):
print('class function')
输出:
_____________________________ MyClass MultiThreading Test______________________________________
arg: 1, id(obj): 40301288
arg: 1, before: {‘a’: ‘a_default’, ‘x’: ‘x_default’}
arg: 1, after: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, id(obj): 40302072
arg: 2, before: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 3, id(obj): 40302296
arg: 3, before: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 3, after: {‘a’: ‘a_3’, ‘x’: ‘x_3’}
arg: 4, id(obj): 40302520
arg: 5, id(obj): 40302744
arg: 5, before: {‘a’: ‘a_3’, ‘x’: ‘x_3’}
arg: 4, before: {‘a’: ‘a_3’, ‘x’: ‘x_3’}
arg: 6, id(obj): 40302968
arg: 4, after: {‘a’: ‘a_4’, ‘x’: ‘x_4’}
arg: 6, before: {‘a’: ‘a_4’, ‘x’: ‘x_4’}
arg: 6, after: {‘a’: ‘a_6’, ‘x’: ‘x_6’}
arg: 5, after: {‘a’: ‘a_5’, ‘x’: ‘x_5’}
arg: 7, id(obj): 40303192
arg: 2, after: {‘a’: ‘a_2’, ‘x’: ‘x_2’}
arg: 7, before: {‘a’: ‘a_2’, ‘x’: ‘x_2’}
arg: 7, after: {‘a’: ‘a_7’, ‘x’: ‘x_7’}
arg: 8, id(obj): 40303416
arg: 8, before: {‘a’: ‘a_7’, ‘x’: ‘x_7’}
arg: 8, after: {‘a’: ‘a_8’, ‘x’: ‘x_8’}
arg: 9, id(obj): 40303640
arg: 9, before: {‘a’: ‘a_8’, ‘x’: ‘x_8’}
arg: 9, after: {‘a’: ‘a_9’, ‘x’: ‘x_9’}
arg: 10, id(obj): 40303864
arg: 10, before: {‘a’: ‘a_9’, ‘x’: ‘x_9’}
arg: 10, after: {‘a’: ‘a_10’, ‘x’: ‘x_10’}
_____________________________ MyClass______________________________________
MyClass: <class ‘singleton_class.MyClass’>, type(MyClass): <class ‘singleton_class.Singleton’>
MyClass.class_attribute(): class_attribute
class function
arg: 1, id(obj): 39514800
arg: 1, before: {‘a’: ‘a_10’, ‘x’: ‘x_10’}
arg: 1, after: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, id(obj): 39945160
arg: 2, before: {‘a’: ‘a_1’, ‘x’: ‘x_1’}
arg: 2, after: {‘a’: ‘a_2’, ‘x’: ‘x_2’}
type(obj): <class ‘singleton_class.MyClass’>
arg: 3, id(obj): 39667864
arg: 3, before: {‘a’: ‘a_2’, ‘x’: ‘x_2’}
arg: 3, after: {‘a’: ‘a_3’, ‘x’: ‘x_3’}
_____________________________ SubClass______________________________________
SubClass: <class ‘__ main__.SubClass’>, type(SubClass): <class ‘singleton_class.Singleton’>
SubClass.class_attribute(): class_attribute
class function
arg: 11, id(obj): 40303584
arg: 11, before: {‘a’: ‘a_default’, ‘x’: ‘x_default’, ‘y’: ‘y_default’}
arg: 11, after: {‘a’: ‘a_11’, ‘x’: ‘x_11’, ‘y’: ‘y_11’}
arg: 12, id(obj): 40303360
arg: 12, before: {‘a’: ‘a_11’, ‘x’: ‘x_11’, ‘y’: ‘y_11’}
arg: 12, after: {‘a’: ‘a_12’, ‘x’: ‘x_12’, ‘y’: ‘y_12’}
type(sub_obj): <class ‘__ main__.SubClass’>
arg: 13, id(obj): 40303248
arg: 13, before: {‘a’: ‘a_12’, ‘x’: ‘x_12’, ‘y’: ‘y_12’}
arg: 13, after: {‘a’: ‘a_13’, ‘x’: ‘x_13’, ‘y’: ‘y_13’}
优点:
- 将单态模式的代码独立封装,MyClass及其子类SubClass无需关心单态模式的实现
- MyClass类有继承的基类OtherClass时不影响单态效果
- 单态模式不影响MyClass类作为基类被继承,且子类SubClass也是单态模式,即SubClass的所有实例都共享SubClass的单态
- 变量MyClass代表的是真正的MyClass类,能使用类属性、类方法
- type(obj) 返回的是真正的MyClass类
- 通过obj3 = type(obj1)() 得到的实例也是单态
- 有 lazy loading 效果
缺点:
- 虽然所有实例的属性都指向同一个内存地址,但每个实例本身都分配了一个内存地址,这也是一种资源浪费
- 需手动设置双重校验锁,才能在保证多线程并发环境下得到的也是单态
参考文档
- 理解 Python对象实例化
- stackoverflow:Python method-wrapper type?
- 类和对象的创建过程(元类,__ new__,__ init__,__ call__)
- 类的继承、元类
- stackoverflow:is-there-a-simple-elegant-way-to-define-singletons
- Python中的单例模式的几种实现方式的及优化
- 5种Python单例模式的实现方式
- Python单体模式的几种常见实现方法详解
- Python 编程,应该养成哪些好的习惯?
- Python单例模式(Singleton)的N种实现
- 单例模式的常见应用场景
- 深入理解设计模式(一):单例模式
- 【Python】闭包的实现原理,如何在内部函数修改外部函数的变量
- 什么是面向对象?为什么要用面向对象编程?
- 怎么从本质上理解面向对象的编程思想?