本文在比较了Python实现单例模式8种方法的优缺点之后,认为在Python中使用元类实现单例模式效果最好,推荐使用元类实现单例。
通常情况下,一个类可以多次实例化,每个实例对象是相互独立的,即类在同一时刻可以有多种实例状态。
如果我们希望:
这时如果某个类在程序运行期间只有一个实例,就能实现上述需求(单例 / 单态)。
最简单的办法就是只实例化一次得到一个实例,自己记住,之后写代码的时候一直用它,不再实例化。问题是自己有可能忘了某个类已经实例化过了,而且别人想复用你的代码时也不知道你已经实例化过了,就很容易发生重复实例化,自然无法确保单例效果。为了解决这个问题,我们希望在这个类本身能够实现无论实例化多少次,每次返回的都是同一个实例的效果,这就是单例模式。这样后续使用这个类的人就无需关注是否已经实例化过,反正重复实例化得到的还是单例。
类():即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个特殊方法:
想要让MyClass成为单例模式,在实例化过程前加上条件判断就可以。问题是在哪里增加条件判断的代码?由于条件判断的代码所在位置不同,单例模式有多种实现方式。本文的重点就是比较分析这些实现方式的优缺点。
为了比较网上比较流行的Python实现单例模式的8种方法,提出以下评价标准:
除了实现方式(一)、(二)以外,都使用以下测试代码:
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 作为获取实例的接口才能获得单例,如果使用 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:
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):
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):
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):
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’}
优点:
缺点:
此实现方式将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:
由于type(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):
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):
优点:
缺点:
此实现方式与(三)使用函数装饰器非常相似,没有本质区别,只不过装饰器的写法不同。
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:
由于type(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):
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):
优缺点与函数装饰器相同,区别在于经过装饰后的变量MyClass代表的是Singleton类(装饰器)的实例,它也不代表真正的MyClass类。
在实现方式(三)、(四)中,经过装饰后的变量MyClass代表的都不是类,不能作为基类被SubClass继承,为了解决这个问题,考虑使用装饰器返回类的实现方式。
由实例化过程原理分析,我们知道了:类的__new__方法是第二个调用的方法,影响创建实例的具体过程;类的__init__方法是最后调用的方法,影响实例的初始化。
那么,通过在类的__new__方法增加条件判断的代码,就能实现单例模式。但是没这么简单,必须要注意三个问题:
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
输出:
_____________________________ 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:
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):
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:
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):
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代表了class_wrapper类,不是真正的MyClass类。为了解决这个问题,考虑使用基类的实现方式。
这种实现方式也是通过在类的__new__方法增加条件判断的代码。但是Singleton是基类,MyClass是子类,基类无法控制子孙类方法的调用,因此必须要注意三个问题:
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:
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):
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:
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):
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’}
优点:
缺点:
实现方式(五)、(六)都是通过在类的__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:
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):
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:
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):
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’}
优点:
缺点:
上述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:
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):
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:
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):
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’}
优点:
缺点: