zoukankan      html  css  js  c++  java
  • 【原创】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
     
  • 相关阅读:
    sublime开启vim模式
    git命令行界面
    搬进Github
    【POJ 2886】Who Gets the Most Candies?
    【UVA 1451】Average
    【CodeForces 625A】Guest From the Past
    【ZOJ 3480】Duck Typing
    【POJ 3320】Jessica's Reading Problemc(尺取法)
    【HDU 1445】Ride to School
    【HDU 5578】Friendship of Frog
  • 原文地址:https://www.cnblogs.com/pengyusong/p/6838407.html
Copyright © 2011-2022 走看看