python 元类的call_【原创】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
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的python 元类的call_【原创】Python 对象创建过程中元类, __new__, __call__, __init__ 的处理...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pythondataframe如何替换值
- 下一篇: python读文件完整代码_python