1. 程式人生 > >Python 的 type 和 object 之間是怎麼一種關係?

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型別的祕密已經說穿了,不一小心連元類也暴露了。哎。慢慢消化吧,資訊量很大。