類別用來產生實例,每個實例都會有個__class__特性,參考至產生它的類別。例如:
>>> class Some:
... pass
...
>>> s = Some()
>>> s.__class__
<class '__main__.Some'>
>>>
... pass
...
>>> s = Some()
>>> s.__class__
<class '__main__.Some'>
>>>
所有的類別,都是type類別的實例,type本身也是個類別,是用type定義產生出來的實例。
有個有趣的事是,產生類別實例的方式,可以透過類別的__call__()來達到相同的目的。例如:
>>> class Some:
... def __init__(self, arg):
... print('__init__')
... self.arg = arg
...
>>> s = Some()
>>> s = Some(10)
__init__
>>> s.arg
10
>>> s2 = Some.__call__(20)
__init__
>>> s2.arg
20
>>>
... def __init__(self, arg):
... print('__init__')
... self.arg = arg
...
>>> s = Some()
>>> s = Some(10)
__init__
>>> s.arg
10
>>> s2 = Some.__call__(20)
__init__
>>> s2.arg
20
>>>
type的三個引數分別是類別的名稱(字串)、類別的父類別(Tuple) 與類別的特性(字典)。type會建立一個物件,該物件擁有__call__()方法,在上例中由Some名稱參考住該物件,如果以Some(10)呼叫 該物件,等同於呼叫Some.__call__(10),而這會呼叫你指定的__init__方法(如果有__new__方法的話,會在__init__ 前呼叫)。
事實上,當你這麼定義類別時:
class Some:
s = 10
def __init__(self, arg):
self.arg = arg
def doSome(self):
self.arg += 1
s = 10
def __init__(self, arg):
self.arg = arg
def doSome(self):
self.arg += 1
Python所作的是剖析完類別的陳述後,建立s參考至10、__init__與doSome參考至function實例,然後呼叫type來建立type實例並指定給Some名稱:
Some = type('Some', (object,), {'__init__' : __init__, 'doSome' : doSome})
在Python中,物件是類別的實例,而類別是type的實例,如果你有方法可以介入type建立實例與初始化的過程,那麼你就有辦法改變類別的行為,這就是meta class的基本概念。
注意,這與 類別修飾器 的概念不同,當你使用類別修飾器時,類別本身已經產生,也就是已經產了type實例了,然後你才去修飾類別的行為,meta class則是直接介入type實例產生與初始化的過程,使用的時機點並不相同。