元类单例如何工作

How a metaclass singleton works

我不清楚 the typical metaclass singleton implementation 是如何工作的。我希望加星标的打印执行两次;它只发生一次:

class _Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        print('Within __call__', cls, cls._instances, *args, **kwargs)
        if cls not in cls._instances:
            print('**About to call __call__', super(_Singleton, cls).__call__, flush=True)
            print("Is cls the '...of object'?", hex(id(cls)).upper())
            cls._instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs)
            print(cls._instances[cls])
        return cls._instances[cls]


class MySingleton(metaclass=_Singleton):
    pass


if __name__ == '__main__':
    print('Making mysingleton')
    mysingleton = MySingleton()
    print("Is mysingleton the 'cls'?", mysingleton)

    print('Making another')
    another = MySingleton()

    # Verify singletude
    assert another == mysingleton

这会打印

Making mysingleton
Within __call__ <class '__main__.MySingleton'> {}
**About to call __call__ <method-wrapper '__call__' of _Singleton object at 0x000001C950C28780>
Is cls the '...of object'? 0X1C950C28780
<__main__.MySingleton object at 0x000001C9513FCA30>
Is mysingleton the 'cls'? <__main__.MySingleton object at 0x000001C9513FCA30>
Making another
Within __call__ <class '__main__.MySingleton'> {<class '__main__.MySingleton'>: <__main__.MySingleton object at 0x000001C9513FCA30>}


根据我对 Python 文档的体验,它们非常循环且令人困惑。文档说 __call__() 在实例被“调用”时被调用。很公平; MySingleton.__call__ 是 运行 因为 mysingleton = MySingleton() 上的“()”表示函数调用。 MySingleton 是 _Singleton 类型,所以它有一个 _instances 字典。字典在第一次调用时自然是空的。条件失败,Super(_Singleton, cls).__call__ 执行。

super() 在这里做什么?从文档中几乎无法理解。它 returns 一个“代理对象”,在其他地方被解释为“一个 'refers' 到共享对象的对象”,它将方法调用委托给 'type' 的父级或兄弟级 class .好的;它将用于调用一些相关的 _Singleton 类型的方法。

此处使用的 super() 的两个参数形式“准确指定参数并进行适当的引用”。那些参考是什么?类型是 _Singleton,对象是 cls,不是 mysingleton0x000001C950C28780 是什么对象。不管怎样,super() 的搜索顺序是 getattr()super()。我认为这意味着根据 _Singleton.__mro__ 查找引用,因为 __call__ 不是属性(或者是?)。也就是说,super() 调用根据 super() 查找,我假设它是 _Singleton。清澈如泥。 __mro__ 产生 (<class '__main__._Singleton'>, <class 'type'>, <class 'object'>)。因此,super(_Singleton, cls) 将寻找“相关的 _Singleton 类型”并调用它的 __call__ 方法;我假设那是 cls.__call__().

因为 cls 是一个 _Singleton,所以我希望看到第二个打印。实际上,我希望有某种递归。两者都不会发生。里面发生了什么?

super 内置函数并不是 Python 语法中最简单的东西。当方法在 classes 的层次结构中被覆盖时使用它,并允许间接指定实际调用哪个版本(在哪个祖先 class 中定义的方法)。

这里,_Singletontype 的子class。很公平。但是由于 _Singleton__call__ 方法已被重写,它必须在其父 class 中调用相同的方法 才能实际构建一个对象.这就是 super(_Singleton, cls).__call__(*args, **kwargs) 的目的:它将调用转发给 _Singleton 的父级。所以它是一样的:

type.__call__(cls, *args, **kwargs)

即:它调用 type__call__ 方法 但仍然使用 cls 作为 self 对象允许创建一个 MySingleton 对象并绕过对 _Singleton.__call__ 的递归调用。替代方案是使用 S.__new__(S, *args, **kwargs) 或直接使用 object.__new__(S),但最后一个会绕过任何可能的对象初始化。

事实上,super是这里的Pythonic方式,因为如果你以后构建一个更复杂的元层次结构classes (_Singleton <- _GenericMeta <- type) , super(_Singleton, cls) 将确保在层次结构中使用 _Singleton 之前的 class。