Python 的 type 和 object 之間是怎麼一種關係?
是初學者請勿噴啊
兩個是互為實例的關係,但不是互為子類的關係,只有type是object的子類,反之則不成立。大牛說兩者是蛋生雞雞生蛋的關係,但我還是不明白,有懂的麻煩解釋一下,希望不要給出外文的鏈接。python為什麼設計出兩個,去掉 一個行不行?
給別人講解過很多次,但寫成文字是第一次。試一試吧,自己主要也是看了這篇文章(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&
在Python的世界中,object是父子關係的頂端,所有的數據類型的父類都是它;type是類型實例關係的頂端,所有對象都是它的實例的。它們兩個的關係可以這樣描述:
- object是一個type,object is and instance of type。即Object是type的一個實例。&>&>&> object.__class__
&&>&>&> type.__bases__
(&此時,白板上對象的關係如下圖:
我們再引入list, dict, tuple 這些內置數據類型來看看:
&>&>&> list.__bases__(&&
再實例化一個list看看:
&>&>&> mylist = [1,2,3]&>&>&> mylist.__class__&把它們加到白板上去:
當我們自己去定個一個類及實例化它的時候,和上面的對象們又是什麼關係呢?試一下:
&>&>&> class C(object):
... pass... &>&>&> C.__class__&實例化
&>&>&> c = C()&>&>&> c.__class__&你以為事情就這樣完了?不。。看見type孤零零在第一列其實不是那麼舒服。。我們給它整幾個玩伴看看。但要怎麼整呢?要屬於第一列的,必須是type的子類,那麼我們只需要繼承type來定義類就可以了:
&>&>&> class M(type):... pass... &>&>&> M.__class__&(&
好了,現在TM這個類就是出現在第二列的。
再總結一下:
第一列,元類列,type是所有元類的父親。我們可以通過繼承type來創建元類。第二列,TypeObject列,也稱類列,object是所有類的父親,大部份我們直接使用的數據類型都存在這個列的。第三列,實例列,實例是對象關係鏈的末端,不能再被子類化和實例化。到現在為止,Python類型的秘密已經說穿了,不一小心連元類也暴露了。哎。慢慢消化吧,信息量很大。
如果轉述版看不懂,那麼去啃一啃原文的吧:(Python Types and Objects)
=============更新=============更新更新。。回答一下題主在問題後面說的為什麼要有兩個,而不是一個。如果type和object只保留一個,那麼一定是object。只有object 時,第一列將不復存在,只剩下二三列,第二列表示類型,第三列表示實例,這個和大部分靜態語言的類型架構類似,如java 。這樣的架構將讓python 失去一種很重要的動態特性--動態創建類型。本來,類(第二列的同學)在Python裡面是一個對象(typeobject),對象是可以在運行時動態修改的,所以我們能在你定義一個類之後去修改他的行為或屬性!拿掉第一列後,第二列變成了純類型,寫成怎樣的,運行時行為就怎樣。在這一點上,並不比靜態語言有優勢。所以,以上!那我就來補充個跟實現思路相關的傳送門吧:先有Class還是先有Object? - RednaxelaFX 的回答&<- 這個傳送門雖然開頭講的是Java的情況,但後面也有講Python和Ruby,相信能解答題主的一些疑惑。
------------------PyObject在 Python 中,所有對象都共有一些特性,這些特性定義在 PyObject 中。PyObject 定義在 Include/object.h 中:
#define PyObject_HEAD
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
typedef struct _object {
PyObject_HEAD
} PyObject;
簡化後可以認為是這樣:
typedef struct _object {
int ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
在 PyObject 中,ob_refcnt 用以記錄對象的引用數(與引用計數的內存回收相關),當有新的指針指向某對象時,ob_refcnt 的值加 1, 當指向某對象的指針刪除時,ob_refcnt 的值減 1,當其值為 0 的時候,則可以將該對象從堆中刪除(事實上並不會立即刪除)。除了 ob_refcnt 之外,還有一個 指向 _typeobject 指針ob_type,這個結構體用於表示對象類型。可以發現, Python 對象的核心在於一個引用計數和一個類型信息。
PyObject 定義的內容會出現在每個對象所佔內存的開始部分。
------------------PyVarObject在 Python 中,除了 bool、float 這樣的定長對象(一旦確定下來需要的內存,便不再有改動),還有另外一種對象:長度可變的對象。這種對象在 Python 的實現中通過 PyVarObject 結構體來表示:
#define PyObject_VAR_HEAD
PyObject_HEAD
Py_ssize_t ob_size; /* Number of items in variable part */
typedef struct {
PyObject_VAR_HEAD
} PyVarObject;
事實上,就是在 PyObject 的基礎上,多了一個 ob_size 變數,用以標識對象的長度(是長度,不是內存佔用)。也就是說,其實 PyVarObject 就是 PyObject 的一個拓展,於是,在 Python 中,所有的對象都可以通過 PyObject * 指針來引用,這一點非常重要,它使得很多操作變得統一(函數參數傳遞等)。
由此,Python 中所有對象在實現的時候,內存無非如下兩種情況:
定長對象 變長對象
+-----------+ +-----------+
| ob_refcnt | | ob_refcnt |
+-----------+ +-----------+
| ob_type | | ob_type |
+-----------+ +-----------+
| | | ob_size |
| | +-----------+
| other | | |
| | | other |
| | | |
+-----------+ +-----------+
在描述 PyObject 的時候,提到了一個 _typeobject 結構體。那麼,它是幹什麼的呢?想像一下,一個對象在創建的時候需要多少內存、這個對象的類名是什麼等等信息,又是如何記錄和區分的呢?
_typeobject(也就是 PyTypeObject)可以被稱之為「指定對象類型的類型對象」,其定義如下:
typedef struct _typeobject {
PyObject_VAR_HEAD
const char *tp_name; /* For printing, in format "&
Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */
// ...... 省略部分暫時不關心的內容
} PyTypeObject;
可以理解為,PyTypeObject 對象是 Python 中面向對象理念中「類」這個概念的實現,這裡只是簡單介紹其定義中的部分內容:
- ty_name:類型名
- tp_basicsize, tp_itemsize:創建類型對象時分配的內存大小信息
- 被省略掉的部分:與該類型關聯的操作(函數指針)
這裡只是簡單描述,上面的內容有些偏頗,暫不必過分深究。
PyType_Type(Python 語言中的 type 對象)的類型就是 PyTypeObject,它是所有 class 的 class,在 Python 中叫做 metaclass。:
PyTypeObject PyType_Type = {
PyVarObject_HEAD_INIT(PyType_Type, 0)
"type", /* tp_name */
sizeof(PyHeapTypeObject), /* tp_basicsize */
sizeof(PyMemberDef), /* tp_itemsize */
(destructor)type_dealloc, /* tp_dealloc */
// ...... 省略了部分內容
};
其實,在實現中 PyType_Type 的 ob_type 指針又指向了自己本身,既是:
PyType_Type
+-----------+&<-------+
| ob_refcnt | |
+-----------+ |
| ob_type +--------+
+-----------+
| |
| other |
| |
+-----------+
class,metaclass,instance,subclass,base
以下成立:
對任意的A,A是instance(推論:任意class也是instance)對任意A,存在B,使得B是A的classA是class 定義為 從A可以創建B(直接或間接),使得B是A的instanceA是B的metaclass 等價於 B是class,且A是B的class(也就是class的class)A是metaclass等價於A是class,且從A創建的instance B是class推論:對任意A是class,存在B使得B是A的metaclasstype的metaclass是type自己對A和B是class,A是B的base等價於B是A的subclass(這裡為了防歧義規定A不是A的subclass,可能與iasubclass不同)
A是B的base,則B不是A的baseA是B的instance當且僅當:B是A的class,或B是A的class的baseobject是所有其他class的base(推論:任意A是object的instance,包括object)推論:沒有類是object的basetype是所有其他metaclass的base推論:object是type的base
type是object的metclasstype是type的class/metaclasstype是type的instanceobject是object的instancetype是object的instanceobject是type的instance注意:metaclass的base不一定是metaclass
class與instance,base與subclass只是兩種正交的關係,它們通過isinstance相互聯繫,在Python中並不要求它們有先後順序
=================================================
我補充一下什麼叫做「不要求有先後順序」,實際上在Python當中自定義類的__bases__,自定義實例的__class__是可以修改的……
&>&>&> class A(object): pass
...
&>&>&> class B(object): pass
...
&>&>&> class C(A): pass
...
&>&>&> c = C()
&>&>&> c.__class__
&
&>&>&> C.__bases__
(&
&>&>&> isinstance(c, C)
True
&>&>&> isinstance(c, A)
True
&>&>&> isinstance(c, B)
False
&>&>&> issubclass(C, A)
True
&>&>&> issubclass(C, B)
False
&>&>&> C.__bases__ = (B,)
&>&>&> issubclass(C, A)
False
&>&>&> issubclass(C, B)
True
&>&>&> isinstance(c, A)
False
&>&>&> isinstance(c, B)
True
&>&>&> c.__class__ = A
&>&>&> isinstance(c, C)
False
&>&>&> isinstance(c, B)
False
&>&>&> isinstance(c, A)
True
本質上來說就是一個指向的關係,想怎麼改就可以怎麼改,只要不違背我上面所說的法則就行。Python里的class和instance就是這樣沒有節操的東西……
我們下面來造一個A是B的metaclass,B是A的base的例子:
&>&>&> class A(type): pass
...
&>&>&> class B(metaclass=A): pass
...
&>&>&> A.__bases__ = (type, B)
&>&>&> isinstance(B, A)
True
&>&>&> issubclass(A, B)
True
&>&>&> isinstance(B, B)
True
由於Python實現的問題,不是所有的__bases__和__class__都可以隨意指定(有一些限制),但大體上可以理解為就是個可以改的指針
謝邀,object是type(object的類型是type),type也是object(type繼承自object)
&>&>&> isinstance(object, type)
True
&>&>&> isinstance(type, object)
True
在python中一切皆object,所有對象都是從object繼承而來的,type也是object,而object的type是type
&>&>&> type.__base__
&
&>&>&> type(object)
&
1、type是一切type的類型2、object是所有對象的基類,type也繼承自object3、object的type是type借用某博客里的一張圖片,他們的關係應該是這樣的(藍色表示繼承,紅色表示類型)去掉object所有對象都不能用了,而去掉type,所有對象還是可以正常運作的,只是沒有一個東西標識他的類型了,至於為什麼可以去掉type,python的type和object之間是怎麼一種關係? - 劉鑫的回答 說的很清楚,這裡就不補充了
其他回答比較詳細了,我就一圖頂千言吧
這部分我當初是讀MSDN里關於C Sharp的Object 和 type 的關係讀懂的……你如果手邊有Jeffery的《CLR via C#》 可以讀一下。專門為了這個問題去買一本可能有點奢侈,不過如果你是 .net 程序員,我推薦你收一本。
簡單的說,很多運行時體系(不一定是針對某種語言)都提供在運行時獲取類型信息的功能,那麼獲取出來的是什麼東西呢?獲取出來的是一個描述類型信息的對象。那麼所有描述類型信息的對象,都是「類型」這個類型的實例。這個type類型不是類型本身,而是用於描述類型的對象實例,類型本身是一種抽象的信息,type類的實例對象是它具體的信息載體。既然type類型也是一種類型,那麼它是公共根類型object的一個派生,也就是合情合理的設計了。
反過來,object 類型並不依賴 type 類型,最多它的一些反射/自省邏輯需要調用type。既不是互為實例:
&>&>&> object.__class__&
&>&>&> type.__class__ &
也不是互為子類:&>&>&> object.__bases__
()
&>&>&> type.__bases__ (&
type和object都是type的實例,而type是object的子類。
__class__是這兩個關係中更基本的一個。所有Python對象都有對應的class對象,而對一個Python的幾乎所有操作,即使是訪問對象屬性o.a這麼基本的,都是轉交給其class對象執行。唯二的例外,是__class__和id,這兩個操作的語義無法被class對象改變。在這方面,Python的可定製程度比絕大部分OOPL要高。
type是所有class對象的class,也稱為metaclass。它決定了你直接操作class對象(而不是通過其實例)時的行為。例如:class C(object):
a = 1
def f(self):
pass
c = C()
c.f() # 行為由C決定
C.a # 行為由type決定
Python並未規定class一定要(直接或間接地)以type作為metaclass。
至於父類-子類關係,之所以說是兩個關係中較弱的一個,因為「一個class有父類」只是metaclass為type的那些class的行為。如果你自己不從type創建一個class(需要使用Python C API),它也可以沒有父類這個概念。
由於type本身也是一個class,並且以自身為metaclass,因此它也以object為祖先。object給所有「以type為(直接或間接的)metaclass創建的class的實例」(實際上包括了所有Python built-in objects和純Python代碼創建的對象)提供了預設行為。至於能否將type和object合併,回顧:
type給所有class對象指定行為object給所有對象提供預設行為type里有一些只對class對象有意義的方法,比如mro()。並且type假設其實例都有class必須定義的一堆屬性(比如class都要有__new__)。如果與object合併成object_type,那麼:所有對象都從object_type繼承了只對class對象有意義的mro等方法所有對象都必須有class對象的一堆屬性,從而內存佔用變大我先放張圖
圖對不對先不管。我們先來看看 type 和 object 分別是什麼。type 實際上是:#define PyVarObject_HEAD_INIT(type, size)
1, type, size,
PyTypeObject PyType_Type = {
PyVarObject_HEAD_INIT(PyType_Type, 0)
"type", /* tp_name */
sizeof(PyHeapTypeObject), /* tp_basicsize */
sizeof(PyMemberDef), /* tp_itemsize */
0, /* tp_base */
...
}
object 實際上是:
PyTypeObject PyBaseObject_Type = {
PyVarObject_HEAD_INIT(PyType_Type, 0)
"object", /* tp_name */
sizeof(PyObject), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_base */
...
}
也就是說,type 和 object 的前兩個欄位是一樣的,都是1和 PyType_Type
ob_refcnt = 1 // 引用計數
ob_type = PyType_Type // 指向一個類型對象,用來指定當前對象的類型
即,type 和 object 的 ob_type 都是 PyType_Type,而PyType_Type就是 type,所以 type 的ob_type 是自己。
我們來看看 isinstance() 做了什麼:#define Py_TYPE(ob) (((PyObject*)(ob))-&>ob_type)
[abstract.c]
PyObject_IsInstance(PyObject *inst, PyObject *cls)
{
static PyObject *name = NULL;
/* Quick test for an exact match */
if (Py_TYPE(inst) == (PyTypeObject *)cls)
return 1;
...
}
isinstance() 做的事情其實很簡單,就是判斷inst -&> ob_type 指向的類型是不是 (PyTypeObject *)cls。
instance(object,type) &<==&> if object.ob_type == type return 1 else return 0;從前面可以看到 ob_type = PyType_Type,而 PyType_Type 就是 type,所以,顯然object 被判斷為了 type 的實例。實際上他們在代碼實現上並沒有類與實例的關係,但是在判斷的時候強加了這層關係。================實例的關係說完了,下面說說繼承關係===================
我們來看看 issubclass():[abstract.c]
PyObject_IsSubclass(PyObject *derived, PyObject *cls)
{
...
return recursive_issubclass(derived, cls);
}
//最終轉化成
[typeobject.c]
int PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b)
{
mro = a-&>tp_mro;
if (mro != NULL) {
...
}
else{
do {
if (a == b)
return 1;
a = a-&>tp_base;
} while (a != NULL);
return b == PyBaseObject_Type; //這一句!
}
}
從上面我們也可以看出 python 的繼承機制。
我們也可以看到,如果一個類沒有基類,那他的父類就是object ,即便在代碼實現上它並沒有基類,但是在 issubclass()中就是這樣認為的!那 type 到底有沒有基類呢?目前我還沒有找到 ,我會繼續找的。即便有也只是給個標誌位標識一下,並沒有什麼特殊的動作。type 和 object 在代碼實現上時平行的,沒有父子類的關係,但是在判斷的時候,開發者將 type 判斷為了 object 的子類,以滿足python 對外呈現的思想。python 對外呈現的思想是:object :是一切類的基類
type :一切類型都是它的實例
但實際上它的代碼實現並不符合這樣的思想,C 語言本身也沒有類和繼承的概念,開發者做了一些處理,讓他們看起來是符合思想的。
總結:type 和 object 其實並沒有繼承與實例化的關係,只是開發者為了保持 python 純正的思想,給他們添加了聯繫。添加聯繫的方式有兩種:一種是設置標誌位,如ob_type,一種是函數判斷中做特殊處理(就像 PyType_IsSubtype 函數那樣)。
最後,我沒看出哪裡有涉及到自舉,哪位大神指點下?- 所有對象都一定是某一個類的實例
- 在python中類也是對象,所有類都是type元類的實例
- type元類也是對象,它是它自己的實例,它的父類是object
- object是類,它是type的實例,object沒有父類
我想這大概是是為了完成完全的面向對象,既然萬物都是對象,那最源頭的對象又是誰的實例?type和object很好地解決了這個問題:抓著自己的鞋帶把自己提起來(bootstrap)。
python新手,說錯請指正。Python中一切皆對象,凡事對象都有類型。從cpython/object.h at 2.7 · python/cpython · GitHub
/* PyObject_HEAD defines the initial segment of every PyObject. */
#define PyObject_HEAD
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
可以看到每一個object中都有一個 ob_type 的指針,指向了某一個具體的類型。
而type本身也是一個對象,不同的object本身的會有一個type上代碼演示一下或許會更清晰一些:In [1]: a = 1
In [2]: type(a)
Out[2]: int
In [3]: type(int)
Out[3]: type
In [4]: type(type)
Out[4]: type
上面的代碼中可以看到 object a,*ob_type 會指向 int,int 是 1 的type,同時也是一個type,而int 本身的*ob_type 則指向了 type
Python中的對象按照比較方便的理解來區分的話,可以分為 普通對象 跟 類型對象,普通對象的 type 就是 int,str 之類的類型, 而類型對象的類型,則是type
In [1]: class NewData(object):
...: pass
...:
In [2]: newdata = NewData()
In [3]: NewData.__class__
Out[3]: type
In [4]: type(NewData)
Out[4]: type
In [5]: newdata.__class__
Out[5]: __main__.NewData
In [6]: type(newdata)
Out[6]: __main__.NewData
In [7]: type.__class__
Out[7]: type
In [8]: type.__base__
Out[8]: object
In [9]: NewData.__base__
Out[9]: object
In [10]: type(object)
Out[10]: type
In [11]: object.__class__
Out[11]: type
之前讀過一篇討論這個問題文章,鏈接已經丟了。。。建議題主讀一讀《Ruby元編程》,這本書對類和對象有較清晰和深入的討論,要求不高,Ruby和Python的對象模型非常相似,不懂Ruby也能讀懂。
要搞懂這個問題,首先要先把類、類型、對象這些概念全部舍掉,重構世界觀。
首先,python裡面一切皆是對象,包括type、object、所有class,全部都是對象,
那對象之間靠什麼聯繫在一起呢?沒錯,靠的就是關係:
Python的世界裡,對象之間一共有兩種關係:1、繼承關係:比如有class A和class B(A),再強調一下,A和B都是對象,則對象B繼承了對象A,或者說A是B的超類,繼承關係可以通過__base__屬性得知,如&>&>&> B.__base__
&
&>&>&> object.__base__
&>&>&> type.__base__
&
&>&>&> a.__class__
&
在python里,對象又可以分為三種:type object、class object和non-class object,
type object指type對象及其子類,如:class TypeA(type): pass
則TypeA和type均屬於type object
class object指obect對象及其子類,如:class A(object):pass
則object和A均屬於class object
non-class object指通過實例化class object得來的對象,如a = A()
則a就是一個non-class object
三者的區別和聯繫在於:
type object都是type的實例,class object是type object的實例,non-class object是class object的實例,type object和class object都可以進行實例化,而non-class object不能進行實例化在python的世界裡,type object和class object這兩種類型的對象都是type對象的實例而type屬於type object,object屬於class object,則顯然:&>&>&> object.__class__
&
&>&>&> type.__class__
&
首先了解2條規則
1.若x是A的一個實例,且A是B的子類,那麼x也是B的一個實例
2.若B是M的實例,且A是B的子類,那麼A也是M的一個實例
首先type為object的子類,(A=type,B=object)
&>&>&> issubclass(type,object) True
其次object是type的實例(x=object,A=type)
&>&>&> isinstance(object,type) True
規則1得出object是object的實例(B=object,M=object)
&>&>&> isinstance(object,object) True
規則2得出type是object的實例
&>&>&> isinstance(type,object) True
同樣可以得到type是type自己實例
&>&>&> isinstance(type,type) True
在Python的C源碼中,用結構體表示Python的類;每個表示類的結構體的第二個欄位都是一個指向PyTypeObject對象的指針;這個PyTypeObject就是類型對象(type得到的東西),每一個類的對象共用一個PyTypeObject對象(所以用的指針)。在python中只要有引用計數(一個long的整型),指向PyTypeObject的指針就可以成為類。
這個。。。怎麼說呢
你要看py2還是py3我這裡說我比較熟的py3
在py3中「萬物皆對象」
object是對象class也是對象type還是對象py3中每個對象都有一個類型(類型也是個對象,也有類型)
object的類型是class name
class的類型是 TypeTypeType的類型是 TypeType換句話來說 class在py3中等於type在Python中每個對象中都包含一個類型對象
// [ object.h ]struct _object { ... struct PyType_Object *tp: ...} PyObject;-----------------------
class A:
passa = A()[ REPL ]:
λ type(A)=&> typeλ type(a)=&> Aλ type(type(a))=&> type嘿嘿,聽著是不是特絕望?
我當初也被繞暈了肥腸抱歉的是,因為移動的逼網出問題了我是在手機上編輯的,所以沒法調好看的縮進和代碼樣式1,理解生活中的栗子
2. python metaclass,即我們說的 class type的兩面性
class type是類(本質是模具,作用是生產磨具),那麼你說他生產的磨具不是他的實例嗎? 而他生產的實例又是類.
[類與實例關係]class type是一種特殊的類,它的實例有兩面性
站在class type角度講, 他的實例有class str,class dict等,也就是class str, class dict是實例. 這些實例最高父類是object.
站在class str,class dict角度講,他們是類, 可以創造各自的實例. 這些實例不存在父類的概念
3.好了,理解了 class type創造出的類有兩面性,那麼在想想生活中的栗子吧. 很多人yy了,靠.越說越複雜.