理解Python中的元类

时间:2017-05-26 07:45:50

标签: python metaclass

我试图让我的头围绕元类,但我仍然无法真正理解它。

据我所知:

任何类本身都是“type”类型的实例 - 因此“调用”类只调用其类的方法__call__ - 恰好是类型__call__type.__call__的效果正是:代码如:

A类:     通过 b = A()

我知道的步骤顺序是:

1. type.__call__接收A类本身作为其第一个参数。

  1. 它调用A.__new__ - 在伪代码中我们可以编写instance = A.__new__(cls)来运行。
  2. 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
    

    根据我的理解,这是一系列步骤:

    1. 由于Spam是MetaOne的一个实例,因此调用X = Spam()将尝试调用不存在的MetaOne类的__call__方法。

    2. 由于MetaOne继承自类型,因此它将调用__call__类型类的方法,并将Spam作为第一个参数。

    3. 之后,调用将在MetaOne类的__new__方法中出现,但它应该包含Spam作为第一个参数。

      MetaOne类的meta参数从哪里开始。

      请帮助我理解。

1 个答案:

答案 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当一个新的(非元)类体被执行时,元类本身的方法。