我试图让我的头围绕元类,但我仍然无法真正理解它。
据我所知:
任何类本身都是“type”类型的实例 - 因此“调用”类只调用其类的方法__call__
- 恰好是类型__call__
。 type.__call__
的效果正是:代码如:
A类: 通过 b = A()
我知道的步骤顺序是:
1. type.__call__
接收A类本身作为其第一个参数。
A.__new__
- 在伪代码中我们可以编写instance = A.__new__(cls)
来运行。3.返回“A”类的实例
4.然后它在实例(__init__
)上调用instance.__init__()
...并返回该实例返回实例
但现在考虑以下代码:
class MetaOne(type):
def __new__(meta, classname, supers, classdict):
print('In MetaOne.new:', meta, classname, supers, classdict, sep='\n...')
return type.__new__(meta, classname, supers, classdict)
class Eggs:
pass
print('making class')
class Spam(Eggs, metaclass=MetaOne):
data = 1
def meth(self, arg):
return self.data + arg
print('making instance')
X = Spam()
print('data:', X.data, X.meth(2))
此脚本的输出如下:
making class
In MetaOne.new:
...<class '__main__.MetaOne'>
...Spam
...(<class '__main__.Eggs'>,)
...{'__qualname__': 'Spam', '__module__': '__main__', 'meth': <function Spam.met
h at 0x00000000010C1D08>, 'data': 1}
making instance
data: 1 3
根据我的理解,这是一系列步骤:
由于Spam是MetaOne的一个实例,因此调用X = Spam()
将尝试调用不存在的MetaOne类的__call__
方法。
由于MetaOne继承自类型,因此它将调用__call__
类型类的方法,并将Spam
作为第一个参数。
之后,调用将在MetaOne类的__new__
方法中出现,但它应该包含Spam
作为第一个参数。
MetaOne类的meta
参数从哪里开始。
请帮助我理解。
答案 0 :(得分:0)
由于Spam是MetaOne的一个实例,因此调用X = Spam()会尝试 调用不存在的MetaOne类的
__call__
方法。
这是您混淆的原因 - 当您创建普通实例时,元类的__call__
(或__new__
和__init__
)不被调用上课。
此外,由于__call__
没有MetaOne
方法,因此通常的继承规则适用:使用MetaOne超类的__call__
方法(它是type.__call__
)
在执行类主体本身时调用元类“__new__
和__init__
方法”(正如您在示例中所见,元类“__new__
中的”print“显示在“制作实例”文本之前。)
创建Span
本身的实例时,不调用元类方法__new__
和__init__
- 调用元类__call__
- 它是执行class's(Span's)__new__
和__init__
。换句话说:元类__call__
负责调用“普通”类的__new__
和__init__
。
由于MetaOne继承自类型,因此会调用调用方法 输入Spam作为第一个参数的类。
确实如此,但你没有发表任何印刷声明来“观察”这种情况:
class MyMeta(type):
def __new__(metacls, name, bases, namespace):
print("At meta __new__")
return super().__new__(metacls, name, bases, namespace)
def __call__(cls, *args, **kwd):
print ("at meta __call__")
return super().__call__(*args, **kwd)
def Egg(metaclass=MyMeta):
def __new__(cls):
print("at class __new__")
如果我将它粘贴在无效的控制台上,此时它会打印:
At meta __new__
然后,进行交互式会话:
In [4]: fried = Egg()
at meta __call__
at class __new__
额外的思维扭曲的事情是:“类型是类型自己的元类”:意味着type
的{{1}}也负责运行__call__
和{{1当一个新的(非元)类体被执行时,元类本身的方法。