元类单例如何工作
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,不是 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 语法中最简单的东西。当方法在 classes 的层次结构中被覆盖时使用它,并允许间接指定实际调用哪个版本(在哪个祖先 class 中定义的方法)。
这里,_Singleton
是 type
的子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。
我不清楚 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,不是 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 语法中最简单的东西。当方法在 classes 的层次结构中被覆盖时使用它,并允许间接指定实际调用哪个版本(在哪个祖先 class 中定义的方法)。
这里,_Singleton
是 type
的子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。