首先我們知道晃洒,實例衍生自類對象,類對象衍生自元類對象
class Demo:
def __new__(cls, *args, **kwargs):
return super().__new__(cls, *args, **kwargs)
def __init__(self):
pass
def __call__(self, *args, **kwargs):
pass
這是一個普通的類對象氧骤,其中
- __ new __() 代表 類對象 產生 實例對象 的過程筹陵。實例對象 在這里被生產并 return 回去
- __ init __() 代表 實例對象 初始化的過程朦佩,也稱構造函數(shù)
- __ call __() 代表 實例對象 被調用的行為
類對象之于實例對象语稠,正如元類對象之于類對象仙畦,他們之間的關系是完全相同的
class DemoMeta(type):
def __new__(mcs, *args, **kwargs):
return super().__new__(mcs, *args, **kwargs)
def __init__(cls, *args, **kwargs):
super().__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs):
return super().__call__(*args, **kwargs)
我們同樣可以推斷出
- __ new __() 代表 元類對象 產生 類對象 的過程慨畸。類對象 在這里被生產并 return 回去
- __ init __() 代表 類對象 初始化的過程先口,也稱構造函數(shù)
- __ call __() 代表 元類對象 被調用的行為
那么上述幾個方法何時被調用碉京?
我們不妨根據(jù)其作用來推斷
-
__ call __()
是被對象被調用時的行為螟深,當我們使用 類對象 來創(chuàng)建新實例時界弧。本質就是調用 類對象 的行為搭综。
例如d = Demo()
兑巾,在此時蒋歌, 類對象 的call就被調用了委煤。那么call產生的結果是什么堂油?,是創(chuàng)建 實例對象 且初始化碧绞。
所以我們就可以推斷出府框,類對象的call實際內容為
def __call__(cls, *args, **kwargs):
instance = cls.__new__(cls)
instance.__init__(*args, **kwargs)
return instance
# return super().__call__(*args, **kwargs)
-
__ new __()
這是我們在metaclass中最常用的方法,他代表的 元類對象 產生其 實例(類對象) 的過程 -
__ init __()
類對象的init并不常見讥邻,其中最大的問題是迫靖,我們定義class的時候,并沒有顯示的傳參過程计维。
實際上我們在類對象中定義的參數(shù)袜香,都會經過init。
class DemoMeta(type):
def __init__(cls, *args, **kwargs):
super().__init__(*args, **kwargs)
class Demo(metaclass=DemoMeta):
num = 10
我們在metaclass的init處打斷點鲫惶,便可以看到num:10在參數(shù)當中
當然我們也可以手動定義類對象蜈首,就像創(chuàng)建實例那樣簡單
Demo = DemoMeta('Demo', (object,), {num: 10})
# 等價于
class Demo(metaclass=DemoMeta):
num = 10
如果按類推,DemoMeta()是調用元類的__ call __()
欠母。
沒錯欢策,是這樣,按理來說這是個無限的循環(huán),當然實際上python解釋器不會允許這樣的情況睛榄。
無論是元類,類旨剥,實例,都不具有特殊性蚕礼。他們都遵循著同樣的行為
唯一的不同是嗡午,有些動作荔睹,python幫我們自行實現(xiàn)了腊尚。例如定義類時劝篷,會自動執(zhí)行元類的實例化。