【发布时间】:2021-08-03 22:49:54
【问题描述】:
我不清楚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 字典。 dict 在第一次调用时自然是空的。条件失败,Super(_Singleton, cls).__call__ 执行。
super() 在这里做什么?从文档中几乎无法理解。它返回一个“代理对象”,在别处解释为“一个'引用'共享对象的对象”,它将方法调用委托给'type'的父类或兄弟类。好的;它将用于调用一些相关的_Singleton类型的方法。
这里使用的 super() 的两个参数形式“准确地指定参数并进行适当的引用”。那些参考是什么?类型是_Singleton,对象是cls,不是mysingleton。这是0x000001C950C28780 的任何对象。无论如何,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,我希望看到第二个打印件。实际上,我期望某种递归。两者都不会发生。里面发生了什么?
【问题讨论】: