如何解决元类单例如何工作
我不清楚 the typical metaclass singleton implementation 是如何工作的。我希望加星标的打印执行两次;它只发生一次:
class _Singleton(type):
_instances = {}
def __call__(cls,*args,**kwargs):
print('Within __call__',cls,cls._instances,**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() 在这里做什么?从文档中几乎无法理解。它返回一个“代理对象”,在别处解释为“一个'引用'共享对象的对象”,它将方法调用委托给'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,我希望看到第二个打印。实际上,我希望有某种递归。两者都不会发生。里面发生了什么?
解决方法
内建的 super
并不是 Python 语法中最简单的东西。当一个方法在类的层次结构中被覆盖时使用,并允许间接指定哪个版本(在哪个祖先类中定义的方法)将被实际调用。
这里,_Singleton
是 type
的子类。很公平。但是,由于 __call__
的 _Singleton
方法已被覆盖,因此必须在其父类中调用相同的方法才能实际构建对象。这就是 super(_Singleton,cls).__call__(*args,**kwargs)
的目的:它将调用转发到 _Singleton
的父级。所以它是一样的:
type.__call__(cls,*args,**kwargs)
即:它调用__call__
的type
方法,但仍然使用cls
作为self
对象,允许创建{{ 1}} 对象并绕过对 MySingleton
的递归调用。替代方法是使用 _Singleton.__call__
或直接使用 S.__new__(S,**kwargs)
,但最后一种会绕过任何可能的对象初始化。
实际上,object.__new__(S)
是这里的 Pythonic 方式,因为如果您以后构建更复杂的元类层次结构(_Singleton super 将确保立即使用该类位于层次结构中的 super(_Singleton,cls)
之前。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。