• 【原创】Python 对象创建过程中元类, __new__, __call__, __init__ 的处理


    原始type:

    type是最原始的元类,其__call__方法是在你使用" t_class = type(classname_string, base_classes_tuple, attributes_dict)" 这种语法来使用时, 在__call__方法内使用又会调用type的__new__和__init__方法来创建classname_string的具体类,并初始化类信息。当type(***)调用完成, classname_string代表的类可以用来创建实例了。

    元类调用过程: 原始type元类同理

    如下流程:假设是MyMeta元类,而不是原始type元类

    例子: MyClass = MyMeta('MyClass', bases, attributes)

    my_meta_type = type(MyMeta)MyClass= my_meta_type.__call__(MyMeta, cls, bases, attributes)
    在__call__中应该是如下操作:
    MyClass = MyMeta.__new__(MyMeta, cls, bases, attributes) meta_class = MyClass.__metaclass__ meta_class.__init__(MyClass, cls, bases, attributes) return MyClass

    最终返回MyClass类

    上述元类有一个很令人迷惑的地方,需要注意到,当你的元类是自定义的元类的时候,假设是MyMeta,此时调用的是MyMeta的父元类type的__call__,所以假设MyMeta自定义了__call__,你要知道当调用MyMeta()的时候,该函数并没有被调用,调用的是type的__call__,你定义MyClass对象实例时才会调用该函数。如果你在MyClass对象中也定义了__call__函数,那么假设你定义了一个MyClass的对象myobj,你使用myobj()形式用法时,调用的是MyClass的__call__ 。

    总结: 元类处理过程:定义一个类时,使用声明或者默认的元类对该类进行创建,对元类求type运算,得到父元类(该类声明的元类的父元类),调用父元类的__call__函数,在父元类的__call__函数中, 调用该类声明的元类的__new__函数来创建对象(该函数需要返回一个对象(指类)实例),然后再调用该元类的__init__初始化该对象(此处对象是指类,因为是元类创建的对象),最终返回该类。

    你可以简单实验以下,自定义俩个元类,该俩个元类是父子关系,在定义一个类,设置使用自定义元类的子元类,发现会调用自定义元类的父元类中call的输出,子元类的call并没有输出,在定义类的对象时才输出了

    例子如下:

    class SuperMeta(type):

        def __call__(metaname, clsname, baseclasses, attrs):

            print 'SuperMeta Called'

            clsob = type.__new__(metaname, clsname, baseclasses, attrs)

            type.__init__(clsob, clsname, baseclasses, attrs)

            return clsob

     

     

    class MyMeta(type):

        __metaclass__ = SuperMeta

        def __call__(cls, *args, **kwargs):

            print 'MyMeta called', cls, args, kwargs

            ob = object.__new__(cls, *args)

            ob.__init__(*args)

            return ob

     

    print 'create class'

     

    class Kls(object):

        __metaclass__ = MyMeta

     

        def __init__(self, data):

            self.data = data

     

        def printd(self):

            print self.data

     

    print 'class created ---------------------'

    # 你会发现定义了 Kls 类后输出了 SuperMeta 父元类的输出

    ik = Kls('arun')

    ik.printd()

    ik2 = Kls('avni')

    ik2.printd()

    # 定义Kls对象实例才真的执行了MyMeta的call

    为什么type会调用自己的呢,因为type的type还是type, 蛋疼一小会……

    附加:

    原始type的__call__应该是参数结构应该是:

      metaname, clsname, baseclasses, attrs

    原始type的__new__

      metaname, clsname, baseclasses, attrs

    原始type的__init__

      class_obj, clsname, baseclasses, attrs

    元类的__new__和__init__影响的是创建类对象的行为,父元类的__call__控制对子元类的 __new__,__init__的调用,就是说控制类对象的创建和初始化。父元类的__new__和__init__由更上层的控制,

        一般来说,原始type是最初的父元类,其__new__和__init__是具有普遍意义的,即应该是分配内存、初始化相关信息等

    元类__call__影响的是创建类的实例对象的行为,此时如果类自定义了__new__和__init__就可以控制类的对象实例的创建和初始化

    __new__和__init__ 影响的是创建对象的行为,当这些函数在元类中时,影响创建的是类;同理,当这俩个函数在普通类中时,影响创建的是普通的对象实例。

    __call__ 影响()调用行为, __call__是在创建类的时候调用,即: class Test(object): __metaclass__=type, 定义类时就是创建类,此时会调用元类的__call__,如果元类有继承,子元类定义时执行的是父元类的__call__。

                 如果是普通类实例化对象,调用的是普通类的__call__

    有点绕啊。。。

    参考:

    http://pythoncentral.io/how-metaclasses-work-technically-in-python-2-and-3/

    http://stackoverflow.com/questions/2608708/what-is-the-difference-between-type-and-type-new-in-python  Florentin的答案

    classKls(object):
        __metaclass__=MyMeta
     
        def__init__(self,data):
            self.data=data
     
        defprintd(self):
            printself.data
     
  • 相关阅读:
    centos7内核优化
    MYSQL存储过程,函数,光标
    牛客网计算机考研复试-KY10-球的半径和体积
    牛客网计算机考研复试-KY11-二叉树的遍历
    #include <graphics.h>的解决
    牛客网计算机考研复试-KY30-进制转换
    牛客网计算机考研复试-KY9-成绩排序
    牛客网计算机考研复试-KY8-整数拆分
    牛客网计算机考研复试-KY4-代理服务器
    牛客网计算机考研复试-KY5-反序输出
  • 原文地址:https://www.cnblogs.com/pengyusong/p/6838407.html
Copyright © 2020-2023  润新知