最近,我使用元类来实现单例。然后,我试着理解这个__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
创建的?
创建__call__
实例时只调用__init__
ClassC
内的ClassC
和ClassB.__call__
},但仅调用<asp:Label runat="server" ID="lbl_OrderDate_DateTime_M" Text="Order Date:">
</asp:Label>
(因为它继承自它)?
答案 0 :(得分:1)
首先,这里是您的代码的简化版本,其中包含一些&#34;噪音&#34;为了便于阅读而删除。
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()
现在你的问题:
为什么cls._instance是 main .ClassA对象(或主要 .ClassA对象)?它应该不是父实例,因为它是用super创建的吗?
不,为什么?在您的情况下,在调用ClassA()
时,您实际上会调用Meta.__call__(ClassA)
。 super(Meta, cls)
将选择Meta.__mro__
中的下一个类type
,然后调用type.__call__(Meta, ClassA)
。 type.__call__(Meta, ClassA)
将依次调用ClassA.__new__()
和ClassA.__init__()
。当然(希望,否则你几乎不能使用继承),你会得到一个ClassA
实例。
作为一般规则:super(CurrentClass, obj_or_cls).something()
将在&#34; parent&#34;中选择something
实施。 class(实际上在__mro__
中的下一个类),但self
(或cls
或其他)第一个参数仍将指向self
(或cls
等等)。 IOW,您正在选择方法的父实现,但此方法仍然将当前对象/类作为第一个参数。
为什么在创建ClassC实例时只调用ClassC中的任何内容(
__call__
和__init__
),而只调用ClassB.__call__
(因为它继承了它)
ClassC.__call__()
未被调用,因为代码中没有任何内容调用它。 ClassC.__metaclass__.__call__()
负责ClassC
实例化的ClassC
。您需要调用ClassC.__call__()
的实例才能调用ClassC.__init__()
。
ClassC
未被调用,因为实际上没有创建ClassB
的实例。这里的原因是您首先创建ClassB
的实例。此时(ClassB
的第一次实例化),_instance
没有属性object
,因此在其父类object
上查找该属性。由于_instance
也没有属性ClassB.__class__
,因此查询将继续Meta
,即_instance
。这个属性的None
属性为ClassB
,因此您创建了一个ClassB._instance
实例并将其绑定到ClassB.__dict__
(在ClassC
中创建属性})。
然后,当您尝试实例化if cls._instance is None
时,_instance
测试将首先在ClassC
上查找ClassC
。此时,_instance
不具有ClassC
属性,因此它会在ClassB
第一个父级(mro中的下一个类)上查找,ClassB
。由于您已经实例化了ClassC._instance
一次,因此查找在此处结束,将ClassB.__dict__["_instance"]
解析为ClassB
,因此您获取的内容已创建的Meta._instance
实例
如果你想要一个有效的实现,你必须摆脱_instance
并在None
中的每个班级上设置Meta.__init__()
到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
中的测试:
class Person:
def __init__(self, name, address):
self.name = name
self.address = address
def say_name(self):
print('Hello, my name is', self.name)
def say_address(self):
print('Hello, my name address', self.address)
p = Person('Swaroop')
q = Person('Duisburg')
p.say_name()
q.say_address()