Python 的 type 和 object 之間是怎麼一種關係?
python有很多內建資料型別,比如int、float、str等,還有type、object,以及我們自定義的類,他們都什麼關係呢?
注意:
此文測試環境是python2.2+,python3有不同不同
給別人講解過很多次,但寫成文字是第一次。試一試吧,自己主要也是看了這篇文章(Python Types and Objects)才懂的。
object 和 type的關係很像雞和蛋的關係,先有object還是先有type沒法說,obejct和type是共生的關係,必須同時出現的。
在看下去之前,也要請先明白,在Python裡面,所有的東西都是物件的概念。
在面向物件體系裡面,存在兩種關係:
父子關係,即繼承關係,表現為子類繼承於父類,如『蛇』類繼承自『爬行動物』類,我們說『蛇是一種爬行動物』,英文說『snake is a kind of reptile』。在python裡要檢視一個型別的父類,使用它的bases屬性可以檢視。
型別例項關係,表現為某個型別的例項化,例如『萌萌是一條蛇』,英文說『萌萌 is an instance of snake』。在python裡要檢視一個例項的型別,使用它的class屬性可以檢視,或者使用type()函式檢視。
這兩種關係使用下面這張圖簡單示意,繼承關係使用實線從子到父連線,型別例項關係使用虛線從例項到型別連線:
我們將使用一塊白板來描述一下Python裡面物件的關係,白板劃分成三列:
先來看看type和object:
>>> object
<type 'object'>
>>> type
<type 'type'>
它們都是type的一個例項,表示它們都是型別物件。
在Python的世界中,object是父子關係的頂端,所有的資料型別的父類都是它;type是型別例項關係的頂端,所有物件都是它的例項的。
它們兩個的關係可以這樣描述:
- object是一個type,object is and instance of type。即Object是type的一個例項。
>>> object .__class__
<type 'type'>
>>> object.__bases__ # object 無父類,因為它是鏈條頂端。
()
- type是一種object, type is kind of object。即Type是object的子類。
>>> type.__bases__
(<type 'object'>,)
>>> type.__class__ # type的型別是自己
<type 'type'>
此時,白板上物件的關係如下圖:
我們再引入list, dict, tuple 這些內建資料型別來看看:
>>> list.__bases__
(<type 'object'>,)
>>> list.__class__
<type 'type'>
>>> dict.__bases__
(<type 'object'>,)
>>> dict.__class__
<type 'type'>
>>> tuple.__class__
<type 'type'>
>>> tuple.__bases__
(<type 'object'>,)
它們的父類都是object,型別都是type。
再例項化一個list看看:
>>> mylist = [1,2,3]
>>> mylist.__class__
<type 'list'>
>>> mylist.__bases__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__bases__'
>>> mylist.__bases__
例項化的list的型別是<type 'list'>
, 而沒有了父類。
把它們加到白板上去:
白板上的虛線表示源是目標的例項,實線表示源是目標的子類。即,左邊的是右邊的型別,而上面的是下面的父親。
虛線是跨列產生關係,而實線只能在一列內產生關係。除了type和object兩者外。
當我們自己去定個一個類及例項化它的時候,和上面的物件們又是什麼關係呢?試一下:
>>> class C(object):
... pass
...
>>> C.__class__
<type 'type'>
>>> C.__bases__
(<type 'object'>,)
>>> c = C()
>>> c.__class__
<class '__main__.C'>
>>> c.__bases__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute '__bases__'
這個例項化的C類物件也是沒有父類的屬性的。
再更新一下白板:
白板上的第一列,目前只有type,我們先把這列的東西叫
Type
。白板上的第二列,它們既是第三列的型別,又是第一列的例項,我們把這列的物件叫
TypeObject
。白板上的第三列,它們是第二列型別的例項,而沒有父類(
__bases__
)的,我們把它們叫Instance
。你以為事情就這樣完了?不。。看見type孤零零在第一列其實不是那麼舒服。。我們給它整幾個玩伴看看。但要怎麼整呢?
要屬於第一列的,必須是type的子類,那麼我們只需要繼承type來定義類就可以了:
>>> class M (type):
... pass
...
>>> M.__class__
<type 'type'>
>>> M.__bases__
(<type 'type'>,)
嗯嗯,M類的型別和父類都是type。這個時候,我們可以把它歸到第一列去。那麼,要怎麼樣例項化M型別呢?例項化後它應該出現在那個列?嗯嗯,好吧,剛才你一不小心建立了一個元類,MetaClass!即類的類。如果你要例項化一個元類,那還是得定義一個類:
>>> class MT(object):
... __metaclass__=M
...
>>> MT.__class__
<class '__main__.M'>
>>> MT.__bases__
(<type 'object'>,)
好了,現在TM這個類就是出現在第二列的。因為__bases__
是object,屬於第二列。
再總結一下:
第一列,元類列,type是所有元類的父親。我們可以通過繼承type來建立元類
第二列,TypeObject列,也稱類列,object是所有類的父親,大部份我們直接使用的資料型別都存在這個列的。
第三列,例項列,例項是物件關係鏈的末端,不能再被子類化和例項化。到現在為止,Python型別的祕密已經說穿了,不一小心連元類也暴露了。哎。慢慢消化吧,資訊量很大。