超级在元类中
super in a metaclass
最近用元类实现了单例。然后,我试图了解这个 __metaclass__
东西是如何工作的。为此,我写了这段代码:
import numpy as np
class Meta(type):
_instance = None
def __init__(cls, *args, **kwargs):
print('meta: init')
#return super(Meta, cls).__init__(*args, **kwargs)
def __new__(cls, *args, **kwargs):
print('meta: new')
return super(Meta, cls).__new__(cls, *args, **kwargs)
def __call__(cls, *args, **kwargs):
print('meta: call')
if cls._instance is None:
cls._instance = super(Meta, cls).__call__(*args, **kwargs)
print(cls._instance.__class__)
return cls._instance
class ClassA():
__metaclass__ = Meta
def __init__(self, *args, **kwargs):
self.val = np.random.randint(1000)
print('classA: init')
def __new__(cls, *args, **kwargs):
print('classA: new')
return super(ClassA, cls).__new__(cls, *args, **kwargs)
def __call__(cls, *args, **kwargs):
print('classA: call')
return super(ClassA, cls).__call__(*args, **kwargs)
class ClassB():
__metaclass__ = Meta
def __init__(self, *args, **kwargs):
print('classB: init')
self.val = np.random.randint(1000)
def __new__(cls, *args, **kwargs):
print('classB: new')
return super(ClassB, cls).__new__(cls, *args, **kwargs)
def __call__(cls, *args, **kwargs):
print('classB: call')
return super(ClassB, cls).__call__(*args, **kwargs)
class ClassC(ClassB):
def __init__(self, *args, **kwargs):
print('classC: init')
super(ClassC, self).__init__(self, *args, **kwargs)
self.test = 3
def __new__(cls, *args, **kwargs):
print('classC: new')
return super(ClassC, cls).__new__(cls, *args, **kwargs)
def __call__(cls, *args, **kwargs):
print('classC: call')
return super(ClassC, cls).__call__(*args, **kwargs)
if __name__ == '__main__':
a1 = ClassA()
b1 = ClassB()
a2 = ClassA()
b2 = ClassB()
c1 = ClassC()
print(a1.val)
print(b1.val)
print(a2.val)
print(b2.val)
我有两个问题:
为什么cls._instance
是__main__.ClassA object
(或__main__.ClassA object
)?它不应该是父实例,因为它是用 super
?
创建的
为什么在创建 ClassC
实例时没有调用 ClassC
中的任何内容(__call__
和 __init__
)而只有 ClassB.__call__
(因为它继承自它)?
首先,这是您的代码的精简版本,为了便于阅读,删除了一些 "noise"。
import random
class Meta(type):
_instance = None
def __call__(cls, *args, **kwargs):
print('meta: call: %s' % cls)
if cls._instance is None:
cls._instance = super(Meta, cls).__call__(*args, **kwargs)
print("meta: call: returning %s@%s" % (cls._instance.__class__, id(cls._instance)))
return cls._instance
class ClassA(object):
__metaclass__ = Meta
def __new__(cls, *args, **kwargs):
print('classA: new')
return super(ClassA, cls).__new__(cls, *args, **kwargs)
def __init__(self, *args, **kwargs):
self.val = random.randint(1, 1000)
print('classA: init')
class ClassB(object):
__metaclass__ = Meta
def __new__(cls, *args, **kwargs):
print('classB: new')
return super(ClassB, cls).__new__(cls, *args, **kwargs)
def __init__(self, *args, **kwargs):
print('classB: init')
self.val = random.randint(1, 1000)
class ClassC(ClassB):
def __new__(cls, *args, **kwargs):
print('classC: new')
return super(ClassC, cls).__new__(cls, *args, **kwargs)
def __init__(self, *args, **kwargs):
print('classC: init')
super(ClassC, self).__init__(self, *args, **kwargs)
self.test = 3
def main():
a1 = ClassA()
b1 = ClassB()
a2 = ClassA()
b2 = ClassB()
c1 = ClassC()
print(a1.val)
print(b1.val)
print(a2.val)
print(b2.val)
if __name__ == '__main__':
main()
现在你的问题:
Why does cls._instance is main.ClassA object (or main.ClassA object)? Should it not be a parent instance as it is created with super?
不,为什么?在您的例子中,当调用 ClassA()
时,您实际上调用了 Meta.__call__(ClassA)
。 super(Meta, cls)
将 select Meta.__mro__
中的下一个 class,即 type
,因此调用 type.__call__(Meta, ClassA)
。 type.__call__(Meta, ClassA)
将依次调用 ClassA.__new__()
和 ClassA.__init__()
。所以当然(希望如此,否则你几乎不能使用继承),你确实得到了一个 ClassA
实例。
作为一般规则:super(CurrentClass, obj_or_cls).something()
将 select something
在 "parent" class 中执行(实际上在下一个 class在 __mro__
) 但 self
(或 cls
或其他)第一个参数仍将指向 self
(或 cls
等)。 IOW,您正在 selecting 一个方法的父实现,但是此方法将仍然 获取当前 object/class 作为第一个参数。
Why nothing (neither __call__
nor __init__
) inside ClassC is called when creating a ClassC instance but only the ClassB.__call__
(as it inherits from it)
ClassC.__call__()
未被调用,因为您的代码中没有任何内容调用它。 ClassC.__metaclass__.__call__()
负责 ClassC
实例化。您需要调用 ClassC
的 实例 才能调用 ClassC.__call__()
。
ClassC.__init__()
没有被调用,因为实际上没有创建 ClassC
的实例。这里的原因是你首先创建了一个ClassB
的实例。此时(ClassB
的第一个实例),ClassB
没有属性 _instance
,因此在其父 class object
上查找属性。由于 object
也没有属性 _instance
,因此继续查找 ClassB.__class__
,即 Meta
。这个确实有一个 _instance
属性,即 None
,因此您确实创建了一个 ClassB
实例并将其绑定到 ClassB._instance
(例如在 [=53= 中创建属性) ]).
然后当您尝试实例化 ClassC
时,if cls._instance is None
测试将首先在 ClassC
上查找 _instance
。此时,ClassC
没有 具有 _instance
属性,因此它在 ClassC
第一个父级(下一个 class mro),即 ClassB
。由于您已经实例化 ClassB
一次,因此查找到此结束,将 ClassC._instance
解析为 ClassB.__dict__["_instance"]
,所以您得到的 是 已经创建的 ClassB
实例。
如果你想要一个有效的实现,你必须摆脱 Meta._instance
并在 Meta.__init__()
中的每个 class 上将 _instance
设置为 None
:
class Meta(type):
def __init__(cls, *args, **kw):
cls._instance = None
def __call__(cls, *args, **kwargs):
print('meta: call: %s' % cls)
if cls._instance is None:
cls._instance = super(Meta, cls).__call__(*args, **kwargs)
print("meta: call: returning %s@%s" % (cls._instance.__class__, id(cls._instance)))
return cls._instance
或将 Meta.__call__()
中的测试替换为 getattr()
:
class Meta(type):
def __call__(cls, *args, **kwargs):
print('meta: call: %s' % cls)
if not getattr(cls, "_instance", None):
cls._instance = super(Meta, cls).__call__(*args, **kwargs)
print("meta: call: returning %s@%s" % (cls._instance.__class__, id(cls._instance)))
return cls._instance
最近用元类实现了单例。然后,我试图了解这个 __metaclass__
东西是如何工作的。为此,我写了这段代码:
import numpy as np
class Meta(type):
_instance = None
def __init__(cls, *args, **kwargs):
print('meta: init')
#return super(Meta, cls).__init__(*args, **kwargs)
def __new__(cls, *args, **kwargs):
print('meta: new')
return super(Meta, cls).__new__(cls, *args, **kwargs)
def __call__(cls, *args, **kwargs):
print('meta: call')
if cls._instance is None:
cls._instance = super(Meta, cls).__call__(*args, **kwargs)
print(cls._instance.__class__)
return cls._instance
class ClassA():
__metaclass__ = Meta
def __init__(self, *args, **kwargs):
self.val = np.random.randint(1000)
print('classA: init')
def __new__(cls, *args, **kwargs):
print('classA: new')
return super(ClassA, cls).__new__(cls, *args, **kwargs)
def __call__(cls, *args, **kwargs):
print('classA: call')
return super(ClassA, cls).__call__(*args, **kwargs)
class ClassB():
__metaclass__ = Meta
def __init__(self, *args, **kwargs):
print('classB: init')
self.val = np.random.randint(1000)
def __new__(cls, *args, **kwargs):
print('classB: new')
return super(ClassB, cls).__new__(cls, *args, **kwargs)
def __call__(cls, *args, **kwargs):
print('classB: call')
return super(ClassB, cls).__call__(*args, **kwargs)
class ClassC(ClassB):
def __init__(self, *args, **kwargs):
print('classC: init')
super(ClassC, self).__init__(self, *args, **kwargs)
self.test = 3
def __new__(cls, *args, **kwargs):
print('classC: new')
return super(ClassC, cls).__new__(cls, *args, **kwargs)
def __call__(cls, *args, **kwargs):
print('classC: call')
return super(ClassC, cls).__call__(*args, **kwargs)
if __name__ == '__main__':
a1 = ClassA()
b1 = ClassB()
a2 = ClassA()
b2 = ClassB()
c1 = ClassC()
print(a1.val)
print(b1.val)
print(a2.val)
print(b2.val)
我有两个问题:
为什么
cls._instance
是__main__.ClassA object
(或__main__.ClassA object
)?它不应该是父实例,因为它是用super
? 创建的
为什么在创建
ClassC
实例时没有调用ClassC
中的任何内容(__call__
和__init__
)而只有ClassB.__call__
(因为它继承自它)?
首先,这是您的代码的精简版本,为了便于阅读,删除了一些 "noise"。
import random
class Meta(type):
_instance = None
def __call__(cls, *args, **kwargs):
print('meta: call: %s' % cls)
if cls._instance is None:
cls._instance = super(Meta, cls).__call__(*args, **kwargs)
print("meta: call: returning %s@%s" % (cls._instance.__class__, id(cls._instance)))
return cls._instance
class ClassA(object):
__metaclass__ = Meta
def __new__(cls, *args, **kwargs):
print('classA: new')
return super(ClassA, cls).__new__(cls, *args, **kwargs)
def __init__(self, *args, **kwargs):
self.val = random.randint(1, 1000)
print('classA: init')
class ClassB(object):
__metaclass__ = Meta
def __new__(cls, *args, **kwargs):
print('classB: new')
return super(ClassB, cls).__new__(cls, *args, **kwargs)
def __init__(self, *args, **kwargs):
print('classB: init')
self.val = random.randint(1, 1000)
class ClassC(ClassB):
def __new__(cls, *args, **kwargs):
print('classC: new')
return super(ClassC, cls).__new__(cls, *args, **kwargs)
def __init__(self, *args, **kwargs):
print('classC: init')
super(ClassC, self).__init__(self, *args, **kwargs)
self.test = 3
def main():
a1 = ClassA()
b1 = ClassB()
a2 = ClassA()
b2 = ClassB()
c1 = ClassC()
print(a1.val)
print(b1.val)
print(a2.val)
print(b2.val)
if __name__ == '__main__':
main()
现在你的问题:
Why does cls._instance is main.ClassA object (or main.ClassA object)? Should it not be a parent instance as it is created with super?
不,为什么?在您的例子中,当调用 ClassA()
时,您实际上调用了 Meta.__call__(ClassA)
。 super(Meta, cls)
将 select Meta.__mro__
中的下一个 class,即 type
,因此调用 type.__call__(Meta, ClassA)
。 type.__call__(Meta, ClassA)
将依次调用 ClassA.__new__()
和 ClassA.__init__()
。所以当然(希望如此,否则你几乎不能使用继承),你确实得到了一个 ClassA
实例。
作为一般规则:super(CurrentClass, obj_or_cls).something()
将 select something
在 "parent" class 中执行(实际上在下一个 class在 __mro__
) 但 self
(或 cls
或其他)第一个参数仍将指向 self
(或 cls
等)。 IOW,您正在 selecting 一个方法的父实现,但是此方法将仍然 获取当前 object/class 作为第一个参数。
Why nothing (neither
__call__
nor__init__
) inside ClassC is called when creating a ClassC instance but only theClassB.__call__
(as it inherits from it)
ClassC.__call__()
未被调用,因为您的代码中没有任何内容调用它。 ClassC.__metaclass__.__call__()
负责 ClassC
实例化。您需要调用 ClassC
的 实例 才能调用 ClassC.__call__()
。
ClassC.__init__()
没有被调用,因为实际上没有创建 ClassC
的实例。这里的原因是你首先创建了一个ClassB
的实例。此时(ClassB
的第一个实例),ClassB
没有属性 _instance
,因此在其父 class object
上查找属性。由于 object
也没有属性 _instance
,因此继续查找 ClassB.__class__
,即 Meta
。这个确实有一个 _instance
属性,即 None
,因此您确实创建了一个 ClassB
实例并将其绑定到 ClassB._instance
(例如在 [=53= 中创建属性) ]).
然后当您尝试实例化 ClassC
时,if cls._instance is None
测试将首先在 ClassC
上查找 _instance
。此时,ClassC
没有 具有 _instance
属性,因此它在 ClassC
第一个父级(下一个 class mro),即 ClassB
。由于您已经实例化 ClassB
一次,因此查找到此结束,将 ClassC._instance
解析为 ClassB.__dict__["_instance"]
,所以您得到的 是 已经创建的 ClassB
实例。
如果你想要一个有效的实现,你必须摆脱 Meta._instance
并在 Meta.__init__()
中的每个 class 上将 _instance
设置为 None
:
class Meta(type):
def __init__(cls, *args, **kw):
cls._instance = None
def __call__(cls, *args, **kwargs):
print('meta: call: %s' % cls)
if cls._instance is None:
cls._instance = super(Meta, cls).__call__(*args, **kwargs)
print("meta: call: returning %s@%s" % (cls._instance.__class__, id(cls._instance)))
return cls._instance
或将 Meta.__call__()
中的测试替换为 getattr()
:
class Meta(type):
def __call__(cls, *args, **kwargs):
print('meta: call: %s' % cls)
if not getattr(cls, "_instance", None):
cls._instance = super(Meta, cls).__call__(*args, **kwargs)
print("meta: call: returning %s@%s" % (cls._instance.__class__, id(cls._instance)))
return cls._instance