type 類別
May 12, 2022Python 可以使用類別來定義物件的藍圖,每個物件實例本身都有 __class__
屬性,參考至實例建構時使用之類別。
類別是 type 實例
類別本身也有 __class__
屬性,那麼它會參考至什麼?
>>> class Some:
... pass
...
>>> s = Some()
>>> s.__class__
<class '__main__.Some'>
>>> Some.__class__
<class 'type'>
>>>
類別的 __class__
參考至 type
類別,類別也是一個物件,是 type
類別的實例。
在〈callable 物件〉談過,類別可以定義 __call__
方法,讓類別實例成為 callable 物件,類別是 type
的實例,type
定義了 __call__
方法,因此要建立類別實例,不需要使用 new
,而是像函式呼叫那,在類別名稱後接上圓括號,你也可以直接呼叫類別的 __call__
方法:
>>> class Some:
... def __init__(self):
... print('__init__')
...
>>> Some()
__init__
<__main__.Some object at 0x000002708FEF1FA0>
>>> Some.__call__()
__init__
<__main__.Some object at 0x000002708FEF1190>
>>>
type
定義了 __call__
,因此每個類別都會有 __call__
方法,其定義的行為中,會呼叫 __new__
、__init__
,因此上例中會看到顯示了 '__init__'
。
透過 type 建立類別
既然每個類別都是 type
類別的實例,那麼有沒有辦法撰寫程式碼,直接使用 type
類別來建構出類別呢?可以!必須先知道的是,使用 type
類別建構類別時,必須指定三個引數,分別是類別名稱(字串)、類別的父類別(tuple
) 與類別的屬性(dict
)。
如果如下定義類別的話:
class Some:
x = 10
def __init__(self, arg):
self.arg = arg
def doSome(self):
self.arg += 1
Python 在剖析完類別之後,會建立 x
名稱參考至 10、建立 __init__
與 doSome
名稱分別參考至各自定義的函式,然後呼叫 type
建立實例,並指定給 Some
名稱,也就是類似於:
>>> def __init__(self, arg):
... self.arg = arg
...
>>> def doSome(self):
... self.arg += 1
...
>>> x = 10
>>> Some = type('Some', (object,), {'x': 10, '__init__' : __init__, 'doSome' : doSome})
>>> s = Some(100)
>>> s.arg
100
>>>
在 Python 中,類別是 type
的實例,如果有方法能介入 type
建立實例與初始化的過程,就有辦法改變類別的行為,這就是 meta 類別的基本概念。
這與裝飾器的概念不同,要對類別使用裝飾器時,類別本身已經產生,也就是已經產生了 type
實例,然後才去裝飾類別的行為;meta 類別是直接介入 type
建構與初始化類別的過程,時機點並不相同。