Python源碼學習之PyObject和PyTypeObject
前言
Python是C語言實現(xiàn)的,因此Python對象在C語言層面應該是一個結構體 ,組織對象占用的內存。 不同類型的對象,數(shù)據(jù)及行為均可能不同,因此可以大膽猜測:不同類型的對象由不同的結構體表示。
對象也有一些共性,比如每個對象都需要有一個引用計數(shù),用于實現(xiàn)垃圾回收機制。因此,還可以進一步猜測:表示對象的結構體有一個公共頭部。
一. 實例對象的基石—PyObject和PyVarObject
PyObject和PyVarObject本質上是對象的頭部信息。
1.1 PyObject結構體
Python對象都由PyObject結構體表示,對象引用則是指針PyObject *。 PyObject結構體定義于頭文件object.h,路徑為Include/object.h,代碼如下
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
對結構體中的元素進行說明,
| 元素名稱 | 說明 |
|---|---|
| ob_refcnt | 引用計數(shù),對象被其他地方引用時加一,引用解除時減一; 當引用計數(shù)為零,便可將對象回收,這是最簡單的垃圾回收機制。 |
| ob_type | 類型指針指向對象的類型對象,類型對象描述實例對象的數(shù)據(jù)及行為。 |
| _PyObject_HEAD_EXTRA | 宏,同樣定義在Include/object.h頭文件內。 |
1.2 宏的定義
#ifdef Py_TRACE_REFS
/* Define pointers to support a doubly-linked list of all live heap objects. */
#define _PyObject_HEAD_EXTRA \
struct _object *_ob_next; \
struct _object *_ob_prev;
#define _PyObject_EXTRA_INIT 0, 0,
#else
#define _PyObject_HEAD_EXTRA
#define _PyObject_EXTRA_INIT
#endif
如果Py_TRACE_REFS被定義,宏展開為兩個指針ob_next和ob_prev用來實現(xiàn)雙向鏈表。注釋中說明,雙向鏈表用于跟蹤所有活躍堆對象,一般不啟用,不深入介紹。
1.3 PyVarObject結構體
用于表示變長對象的PyVarObject結構體是在PyObject結構體的基礎上加入長度信息。
typedef struct {
PyObject ob_base;
Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;
相比object結構體增加了ob_size字段用于記錄元素個數(shù)。

1.4 兩種頭部信息宏定義及其初始化
具體實例對象視其內存大小是否固定,決定其屬于定長對象還是變長對象。相應的需要具有頭部信息PyObject或PyVarObject。
因此,頭文件準備了兩個頭部信息的宏定義PyObject_HEAD和PyObject_VAR_HEAD,方便對象使用,
#define PyObject_HEAD PyObject ob_base; #define PyObject_VAR_HEAD PyVarObject ob_base;
宏定義說明,
#define PyObject_HEAD PyObject ob_base; 表示將代碼中其他出現(xiàn)PyObject_HEAD的地方,替換成PyObject ob_base;
1.4.1 定長對象實現(xiàn)
內存大小固定的浮點數(shù)類的實現(xiàn)只需在PyObject頭部基礎上,用一個雙精度浮點數(shù)double加以實現(xiàn),
typedef struct {
PyObject_HEAD
double ob_fval;
} PyFloatObject;

1.4.2 變長對象實現(xiàn)
內存大小不固定的列表對象則需要在PyVarObject頭部的基礎上,用一個動態(tài)數(shù)組加以實現(xiàn),數(shù)組存儲列表包含的對象,即 PyObject 指針,
typedef struct {
PyObject_VAR_HEAD
PyObject **ob_item;
Py_ssize_t allocated;
} PyListObject;

PyListObject底層由一個數(shù)組實現(xiàn),關鍵字段是以下3個,
| 字段 | 說明 |
|---|---|
| ob_item | 指向動態(tài)數(shù)組的指針,數(shù)組保存元素對象指針。 |
| allocated | 動態(tài)數(shù)組總長度,即列表當前的 容量。 |
| ob_size | 當前元素個數(shù),即列表當前的 長度。 |
列表容量不足時,Python會自動擴容,具體機制見list源碼解讀。
1.4.3 頭部信息宏初始化
PyObject_HEAD_INIT用于定長對象頭部信息初始化。將引用計數(shù)ob_refcnt設置為1并將對象類型ob_type設置成給定類型。
#define PyObject_HEAD_INIT(type) \
{ _PyObject_EXTRA_INIT \
1, type },
PyVarObject_HEAD_INIT用于變長對象頭部信息初始化。在前者基礎上進一步設置長度字段ob_size。
#define PyVarObject_HEAD_INIT(type, size) \
{ PyObject_HEAD_INIT(type) size },
在源碼中經常見到這兩個宏定義。
二. 類型對象的基石—PyTypeObject 2.1 PyTypeObject包含信息
PyObject記錄了Python中所有對象共有的信息。如引用計數(shù)、類型指針和變長對象特有的元素個數(shù)。但是還有一些細節(jié)需要考慮,
- 創(chuàng)建不同類型的對象時如何得知對象所需的內存信息
- 給定某個對象,如何判斷它支持什么操作
這些作為對象的元信息 ,應該由一個獨立實體保存,與對象所屬類型密切相關。PyObject中包含的ob_type指針,指向一個類型對象。類型對象PyTypeObject也在Include/object.h中定義,關鍵字段如下,
typedef struct _typeobject {
PyObject_VAR_HEAD
const char *tp_name; /* For printing, in format "<module>.<name>" */
Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */
/* Methods to implement standard operations */
destructor tp_dealloc;
printfunc tp_print;
getattrfunc tp_getattr;
setattrfunc tp_setattr;
// ...
/* Attribute descriptor and subclassing stuff */
struct _typeobject *tp_base;
// ......
} PyTypeObject;
類型對象PyTypeObject是一個變長對象,包含變長對象頭部信息PyObject_VAR_HEAD和專有字段,
| 字段 | 說明 |
|---|---|
| 類型名稱 | tp_name字段 |
| 類型的繼承信息 | tp_base字段指向基類對象 |
| 創(chuàng)建實例對象時所需的內存信息 | tp_basicsize 和 tp_itemsize 字段 |
| 該類型支持的相關操作信息 | tp_print、tp_getattr等函數(shù)指針 |
PyTypeObject就是類型對象在 Python 中的表現(xiàn)形式,對應著面向對象中“類”的概念。PyTypeObject結構很復雜,目前只需要知道它保存著對象的元信息,描述對象的類型即可。
2.2 類型對象和實例對象在內存中的關系
以float為例,考察類型對象和實例對象在內存中的形態(tài)和關系,
>>> float <class 'float'> >>> pi = 3.14 >>> e = 2.71 >>> type(pi) is float True

- 兩個float實例對象都是
PyFloatObject結構體,除了公共頭部字段ob_refcnt和ob_type,專有字段ob_fval保存了對應的數(shù)值。 - 類型對象是一個
PyTypeObject結構體,保存了類型名、內存分配信息以及浮點數(shù)相關操作。實例對象的ob_type字段指向類型對象,Python 據(jù)此判斷對象類型,進而獲悉關于對象的元信息。 - float、pi以及e等變量只是一個指向實際對象的指針。
上圖的內容并不完全正確,更深入的解讀見后一篇博文。
到此這篇關于Python源碼學習之PyObject和PyTypeObject的文章就介紹到這了,更多相關PyObject和PyTypeObject內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
python django 增刪改查操作 數(shù)據(jù)庫Mysql
下面小編就為大家?guī)硪黄猵ython django 增刪改查操作 數(shù)據(jù)庫Mysql。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07
詳解如何在pyqt中通過OpenCV實現(xiàn)對窗口的透視變換
這篇文章主要介紹了如何在pyqt中通過OpenCV實現(xiàn)對窗口的透視變換,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-09-09
pandas進行數(shù)據(jù)的交集與并集方式的數(shù)據(jù)合并方法
今天小編就為大家分享一篇pandas進行數(shù)據(jù)的交集與并集方式的數(shù)據(jù)合并方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-06-06
在pycharm中關掉ipython console/PyDev操作
這篇文章主要介紹了在pycharm中關掉ipython console/PyDev操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-06

