Python实现单例模式8种方法的优缺点比较

转自 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__方法增加条件判断的代码,就能实现单例模式。但是没这么简单,必须要注意三个问题:

  1. 每次调用__new__方法之后,会紧接着调用__init__方法。所以,必须给出一个标志,避免__init__方法重复调用导致成员变量被默认值覆盖。
  2. 多线程并发环境下,如果MyClass类的__init__方法中有耗时操作,则装饰器内的__init__方法也要加双重校验锁。
  3. 经过装饰后的变量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是子类,基类无法控制子孙类方法的调用,因此必须要注意三个问题:

  1. 每次调用__new__方法之后,会紧接着调用__init__方法。所以,必须给出一个标志,避免__init__方法重复调用导致成员变量被默认值覆盖。
  2. 基类Singleton无法控制子孙类方法的调用,如果子孙类有自己特殊的成员变量需要初始化,则__init__方法不能封装到基类Singleton中。
  3. 多线程并发环境下,如果__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 效果

缺点:

  • 虽然所有实例的属性都指向同一个内存地址,但每个实例本身都分配了一个内存地址,这也是一种资源浪费
  • 需手动设置双重校验锁,才能在保证多线程并发环境下得到的也是单态

参考文档

  1. 理解 Python对象实例化
  2. stackoverflow:Python method-wrapper type?
  3. 类和对象的创建过程(元类,__ new__,__ init__,__ call__)
  4. 类的继承、元类
  5. stackoverflow:is-there-a-simple-elegant-way-to-define-singletons
  6. Python中的单例模式的几种实现方式的及优化
  7. 5种Python单例模式的实现方式
  8. Python单体模式的几种常见实现方法详解
  9. Python 编程,应该养成哪些好的习惯?
  10. Python单例模式(Singleton)的N种实现
  11. 单例模式的常见应用场景
  12. 深入理解设计模式(一):单例模式
  13. 【Python】闭包的实现原理,如何在内部函数修改外部函数的变量
  14. 什么是面向对象?为什么要用面向对象编程?
  15. 怎么从本质上理解面向对象的编程思想?
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值