解释 Python 单例类

8
class Singleton:

    instance = None

    def __new__(cls):
        if cls.instance is None:
            cls.instance = super().__new__(cls)
        return cls.instance

singleton_obj1 = Singleton()
singleton_obj2 = Singleton()

print(singleton_obj1)
print(singleton_obj2)

输出

<__main__.Singleton object at 0x10dbc0f60>
<__main__.Singleton object at 0x10dbc0f60>

有人可以解释一下在这一行cls.instance = super().__new__(cls)发生了什么。哪些代码行帮助创建了这个类Singleton


1
顺便说一句,那是一个相当糟糕的单例模式实现。每次调用 'Singleton()' 时它都会调用 'init',因此您的单例将不断重置其属性。(如果您有一个 'init' 方法) - Aran-Fey
@Aran-Fey,你能提供一些更好的实现链接吗? - lch
1
请参考以下链接:https://dev59.com/gGw15IYBdhLWcg3wMpAY#6798042 - Aran-Fey
1
__new__ 不同于 __init__。实际上,对于这个实现来说,__new__ 才是正确的魔法方法,因为如果已经存在一个实例,单例就不需要被初始化。这里有一篇博客解释了它们之间的区别:http://howto.lintel.in/python-__new__-magic-method-explained/ - VoNWooDSoN
2个回答

10

好的,让我们详细地解释一下:

class Singleton: 隐式继承于Object,因为在Python中,所有东西都是一个对象。

instance = None 只在模块加载时读取一次,并将类级变量instance设置为None,但它可以被Singleton类的各个实例覆盖。

def __new__(cls):
    if cls.instance is None:
        cls.instance = super().__new__(cls)
    return cls.instance

首先,我觉得他们在其中加入“cls”很奇怪,我知道他们为什么这样做,因为它是指类overall而不是该类的特定实例。但对于不知道自己在看什么的人来说,这可能会让人感到困惑。无论如何...

__new__是一个魔法函数,在调用__init__之前被调用,大致相当于为类的新实例分配空间。在这里,cls.instance在模块加载时设置为None,因此每次创建新的Singleton时都会进行此测试。由于cls.instance在创建Singleton时第一次设置,因此super().__new__(cls)也只被调用了一次Singleton的超类是Object。因此,super().__new__(cls)的行为与您创建的任何其他类的行为完全相同。
如果要创建第二个(或第三/第四/第五...)Singleton实例,则不会调用此super().__new__(cls)。相反,将返回类级变量instance,这意味着不能创建新的Singleton实例

当您打印Singleton实例时,您可以看到0x10dbc0f60的内存地址对于两个“实例”是相同的,因为cls.instance__new__时间返回,您可以将其视为内存分配时间,在调用__init__之前。

这是一种很好的单例模式,因为现在,要创建一个单例类,你只需要从Singleton继承,而繁重的工作已经完成了。你可以像平常一样使用__init__,不必担心任何问题。每次你想要创建一个“新”的该类实例时,你都会得到相同的实例。完全在幕后。

这是相当高级的东西,但看看它有多简单。Python是最好的!


1
非常感谢!解释得非常清楚。 - Ahmad Karim

9
构造函数说:
If there is no instance recorded, 
   create an instance and record it
return the recorded instance

这是一个标准的单例设计模式,适用于大多数编程语言,以确保只创建一个类的实例。

为什么singleton_obj1和singleton_obj2没有获得instance的独立副本? - lch
3
因为instance是一个类变量,而不是实例变量。 - Aran-Fey
2
当第二次调用Singleton()时,instance不是None,因此if语句不会继续创建新实例。之前的实例将被返回。 - quamrana
因为单例模式的定义是始终返回相同的实例。例如,工厂类应该只被构造一次,但可以在多个地方使用,因此应该将其设置为单例模式。 - Dragonthoughts

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接