1. 程式人生 > >【原創】Python 對象創建過程中元類, __new__, __call__, __init__ 的處理

【原創】Python 對象創建過程中元類, __new__, __call__, __init__ 的處理

diff regular luci 自定義 weight ica 一般來說 att ray

原始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對象實例時才會調用該函數。

總結: 元類處理過程:定義一個類時,使用聲明或者默認的元類對該類進行創建,對元類求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__就可以控制類的對象實例的創建和初始化

參考:

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

【原創】Python 對象創建過程中元類, __new__, __call__, __init__ 的處理