知乎上看到的提問:
兩個是互為執行個體的關系,但不是互為子類的關系,隻有type是object的子類,反之則不成立。
大牛說兩者是蛋生雞雞生蛋的關系,但我還是不明白,有懂的麻煩解釋一下,
希望不要給出外文的連結。python為什麼設計出兩個,去掉一個行不行?
下面是jeff kit的回答:
給别人講解過很多次,但寫成文字是第一次。試一試吧,自己主要也是看了這篇文章(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
它們都是type的一個執行個體,表示它們都是類型對象。
在Python的世界中,object是父子關系的頂端,所有的資料類型的父類都是它;type是類型執行個體關系的頂端,所有對象都是它的執行個體的。它們兩個的關系可以這樣描述:
- object是一個type,object is an instance of type。即Object是type的一個執行個體。
>>> object.__class__
>>> object.__bases__ # object 無父類,因為它是鍊條頂端。
()
- type是一種object, type is kind of object。即Type是object的子類。
>>> type.__bases__
(,)
>>> type.__class__ # type的類型是自己
此時,白闆上對象的關系如下圖:
我們再引入list, dict, tuple 這些内置資料類型來看看:
>>> list.__bases__
(,)
>>> list.__class__
>>> dict.__bases__
(,)
>>> dict.__class__
>>> tuple.__class__
>>> tuple.__bases__
(,)
它們的父類都是object,類型都是type。
再執行個體化一個list看看:
>>> mylist = [1,2,3]
>>> mylist.__class__
>>> mylist.__bases__
Traceback (most recent call last):
File "", line 1, in
AttributeError: ‘list‘ object has no attribute ‘__bases__‘
執行個體化的list的類型是, 而沒有了父類。
把它們加到白闆上去:
白闆上的虛線表示源是目标的執行個體,實線表示源是目标的子類。即,左邊的是右邊的類型,而上面的是下面的父親。
虛線是跨列産生關系,而實線隻能在一列内産生關系。除了type和object兩者外。
當我們自己去定個一個類及執行個體化它的時候,和上面的對象們又是什麼關系呢?試一下:
>>> class C(object):
... pass
...
>>> C.__class__
>>> C.__bases__
(,)
執行個體化
>>> c = C()
>>> c.__class__
>>> c.__bases__
Traceback (most recent call last):
File "", line 1, in
AttributeError: ‘C‘ object has no attribute ‘__bases__‘
這個執行個體化的C類對象也是沒有父類的屬性的。
再更新一下白闆:
白闆上的第一列,目前隻有type,我們先把這列的東西叫Type。
白闆上的第二列,它們既是第三列的類型,又是第一列的執行個體,我們把這列的對象叫TypeObject。
白闆上的第三列,它們是第二列類型的執行個體,而沒有父類(__bases__)的,我們把它們叫Instance。
你以為事情就這樣完了?不。。看見type孤零零在第一列其實不是那麼舒服。。我們給它整幾個玩伴看看。但要怎麼整呢?要屬于第一列的,必須是type的子類,那麼我們隻需要繼承type來定義類就可以了:
>>> class M(type):
... pass
...
>>> M.__class__
>>> M.__bases__
(,)
>>>
嗯嗯,M類的類型和父類都是type。這個時候,我們可以把它歸到第一列去。那麼,要怎麼樣執行個體化M類型呢?執行個體化後它應該出現在那個列?嗯嗯,好吧,剛才你一不小心建立了一個元類,MetaClass!即類的類。如果你要執行個體化一個元類,那還是得定義一個類:
>>> class TM(object):
... __metaclass__ = M # 這樣來指定元類。
...
...
>>> TM.__class__
# 這個類不再是type類型,而是M類型的。
>>> TM.__bases__
(,)
好了,現在TM這個類就是出現在第二列的。
再總結一下:
第一列,元類列,type是所有元類的父親。我們可以通過繼承type來建立元類。
第二列,TypeObject列,也稱類列,object是所有類的父親,大部份我們直接使用的資料類型都存在這個列的。
第三列,執行個體列,執行個體是對象關系鍊的末端,不能再被子類化和執行個體化。
到現在為止,Python類型的秘密已經說穿了,不一小心連元類也暴露了。哎。慢慢消化吧,資訊量很大。
=============更新=============
更新更新。。回答一下題主在問題後面說的為什麼要有兩個,而不是一個。如果type和object隻保留一個,那麼一定是object。隻有object
時,第一列将不複存在,隻剩下二三列,第二清單示類型,第三清單示執行個體,這個和大部分靜态語言的類型架構類似,如java
。這樣的架構将讓python
失去一種很重要的動态特性–動态建立類型。本來,類(第二列的同學)在Python裡面是一個對象(typeobject),對象是可以在運作時動态修改的,是以我們能在你定義一個類之後去修改他的行為或屬性!拿掉第一列後,第二列變成了純類型,寫成怎樣的,運作時行為就怎樣。在這一點上,并不比靜态語言有優勢。是以,以上!
=============補充=============
補充一個知乎上另一個人對該問題的簡要回答
作者:劉鑫
簡單的說,很多運作時體系(不一定是針對某種語言)都提供在運作時擷取類型資訊的功能,那麼擷取出來的是什麼東西呢?擷取出來的是一個描述類型資訊的對象。那麼所有描述類型資訊的對象,都是“類型”這個類型的執行個體。這個type類型不是類型本身,而是用于描述類型的對象執行個體,類型本身是一種抽象的資訊,type類的執行個體對象是它具體的資訊載體。既然type類型也是一種類型,那麼它是公共根類型object的一個派生,也就是合情合理的設計了。反過來,object
類型并不依賴 type 類型,最多它的一些反射/自省邏輯需要調用type。