美文网首页
失效的单例模式

失效的单例模式

作者: Yucz | 来源:发表于2020-09-16 00:00 被阅读0次

    简书上为首发,拒绝抄袭,原文地址 https://www.jianshu.com/p/a7f1e58f5ee6

    失效的单例模式

    Python的单例模式实现方法有很多种,其中有一种是基于 __new__ 和让子类去继承的实现方法。但是根据我的踩坑经验,这种方法往往是有问题的。试了下网上的多种实现方法,都有问题! 这种实现的单例并不是真正的单例! 这种实现在单线程中都存在问题,更别说在复杂的多线程环境中了

    代码实现

    我们就拿 stackoverflow高赞回答作分析(网上的其他回答都大同小异),其中所提到的 基于 __new__ 的实现代码如下

    class Singleton(object):
      _instances = {}
      def __new__(class_, *args, **kwargs):
        if class_ not in class_._instances:
            class_._instances[class_] = \
            super(Singleton, class_).__new__(class_, *args, **kwargs)
        return class_._instances[class_]
    
    class MyClass(Singleton):
      pass
    
    c = MyClass()
    

    实现的原理在于,控制子类的 __new__ 方法,让其返回的都是同一个对象

    初步测试

    我们不妨写一些测试代码去分析

    class A(Singleton):
        def __init__(self):
            self.test_list = []
    
    a = A()
    b = A()
    c = A()
    
    a.test_list.append('a')
    b.test_list.append('b')
    c.test_list.append('c')
    
    print(id(a), id(b), id(c))
    print(a is b and b is c)
    print("a.test_list =", a.test_list)
    print("b.test_list =", b.test_list)
    print("c.test_list =", c.test_list)
    

    输出

    140072986470664 140072986470664 140072986470664
    True
    a.test_list = ['a', 'b', 'c']
    b.test_list = ['a', 'b', 'c']
    c.test_list = ['a', 'b', 'c']
    

    输出的确是正常的,我们可以看出 ab 以及 c 都是同一对象。并且我们后续分别往 abc 中的 test_list 都存入数据后,每个实例的 test_list 都是 ['a', 'b', 'c'], 这就是我们想要的结果!

    但这是否意味着这个单例的实现没问题呢?我们再做如下实验

    进一步测试

    我们把对每个实例的 test_list 插入顺序,改成实例化完,立马插入

    看下结果会怎样,测试代码如下

    class A(Singleton):
        def __init__(self):
            self.test_list = []
    
    a = A()
    a.test_list.append('a')
    b = A()
    b.test_list.append('b')
    c = A()
    c.test_list.append('c')
    
    print(id(a), id(b), id(c))
    print(a is b and b is c)
    print("a.test_list =", a.test_list)
    print("b.test_list =", b.test_list)
    print("c.test_list =", c.test_list)
    

    输出

    140373892725000 140373892725000 140373892725000
    True
    a.test_list = ['c']
    b.test_list = ['c']
    c.test_list = ['c']
    

    这时我们就发现问题了!

    abc 还是同一个对象,但是我们往 ab 中塞进的数据竟然不见了,最终的 test_list 只有 c ! 单例失效了! 这个单例的实现有问题!

    原因分析

    这个问题出现的原因就在于使用 __new__ 实现的 Singlegon ,只会控制子类的 __new__ 实现,而不会控制子类的 __init__ 的实现

    当我们的子类自实现 __new__ 方法时,每一次实例化子类,虽然子类的 __new__ 确保最终只会实例化一次,但是 __new__ 执行完后,子类还会执行自己的 __init__ 方法,当__init__ 中存在属性时,属性就会被多次声明,最终新生成的属性会覆盖掉上一次生成的属性!

    我们可以针对上述的代码加一些打印进行跟踪

    代码如下

    class A(Singleton):
        def __init__(self):
            self.test_list = []
            print("id(test_list) =", id(self.test_list))
    
    a = A()
    a.test_list.append('a')
    b = A()
    b.test_list.append('b')
    c = A()
    c.test_list.append('c')
    
    print("id(a.test_list) =", id(a.test_list))
    print("id(b.test_list) =", id(a.test_list))
    print("id(c.test_list) =", id(c.test_list))
    
    print("a.test_list =", a.test_list)
    print("b.test_list =", b.test_list)
    print("c.test_list =", c.test_list)
    

    输出

    id(test_list) = 139866765387336 # a 的 test_list 
    id(test_list) = 139866765389512 # b 的 test_list
    id(test_list) = 139866765387336 # c 的 test_list
    id(a.test_list) = 139866765387336 # 打印的是 c 的 test_list
    id(b.test_list) = 139866765387336 # 打印的是 c 的 test_list
    id(c.test_list) = 139866765387336 # 打印的是 c 的 test_list
    a.test_list = ['c']
    b.test_list = ['c']
    c.test_list = ['c']
    

    我们可以看出,在 __init__ 中打印 id(self.test_list) 时,执行了三次,说明 __init__ 方法执行了三次

    并且 每个实例的 id(test_list) 是不一样的,可以看出 self.test_list 确实被定义了三次

    而在 a b c 实例化完成后,我们再去打印每个实例的 id(test_list) 时,发现 abtest_list 的 id 竟然变成和 c 一致,即 ab 中的 test_list 被覆盖了,原先的各自的内容都丢失了!

    这明显不符合单例模式的应用场景! 单例模式的这种实现有 BUG!

    总结
    • 单例模式不要使用基于 __new__ 的实现

    相关文章

      网友评论

          本文标题:失效的单例模式

          本文链接:https://www.haomeiwen.com/subject/honqyktx.html