Python源碼學(xué)習(xí)之PyObject和PyTypeObject
前言
Python是C語(yǔ)言實(shí)現(xiàn)的,因此Python對(duì)象在C語(yǔ)言層面應(yīng)該是一個(gè)結(jié)構(gòu)體 ,組織對(duì)象占用的內(nèi)存。 不同類型的對(duì)象,數(shù)據(jù)及行為均可能不同,因此可以大膽猜測(cè):不同類型的對(duì)象由不同的結(jié)構(gòu)體表示。
對(duì)象也有一些共性,比如每個(gè)對(duì)象都需要有一個(gè)引用計(jì)數(shù),用于實(shí)現(xiàn)垃圾回收機(jī)制。因此,還可以進(jìn)一步猜測(cè):表示對(duì)象的結(jié)構(gòu)體有一個(gè)公共頭部。
一. 實(shí)例對(duì)象的基石—PyObject和PyVarObject
PyObject和PyVarObject本質(zhì)上是對(duì)象的頭部信息。
1.1 PyObject結(jié)構(gòu)體
Python對(duì)象都由PyObject
結(jié)構(gòu)體表示,對(duì)象引用則是指針PyObject *
。 PyObject
結(jié)構(gòu)體定義于頭文件object.h,路徑為Include/object.h
,代碼如下
typedef struct _object { _PyObject_HEAD_EXTRA Py_ssize_t ob_refcnt; struct _typeobject *ob_type; } PyObject;
對(duì)結(jié)構(gòu)體中的元素進(jìn)行說(shuō)明,
元素名稱 | 說(shuō)明 |
---|---|
ob_refcnt | 引用計(jì)數(shù),對(duì)象被其他地方引用時(shí)加一,引用解除時(shí)減一; 當(dāng)引用計(jì)數(shù)為零,便可將對(duì)象回收,這是最簡(jiǎn)單的垃圾回收機(jī)制。 |
ob_type | 類型指針指向?qū)ο蟮念愋蛯?duì)象,類型對(duì)象描述實(shí)例對(duì)象的數(shù)據(jù)及行為。 |
_PyObject_HEAD_EXTRA | 宏,同樣定義在Include/object.h頭文件內(nèi)。 |
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
被定義,宏展開(kāi)為兩個(gè)指針ob_next
和ob_prev
用來(lái)實(shí)現(xiàn)雙向鏈表。注釋中說(shuō)明,雙向鏈表用于跟蹤所有活躍堆對(duì)象,一般不啟用,不深入介紹。
1.3 PyVarObject結(jié)構(gòu)體
用于表示變長(zhǎng)對(duì)象的PyVarObject
結(jié)構(gòu)體是在PyObject
結(jié)構(gòu)體的基礎(chǔ)上加入長(zhǎng)度信息。
typedef struct { PyObject ob_base; Py_ssize_t ob_size; /* Number of items in variable part */ } PyVarObject;
相比object
結(jié)構(gòu)體增加了ob_size
字段用于記錄元素個(gè)數(shù)。
1.4 兩種頭部信息宏定義及其初始化
具體實(shí)例對(duì)象視其內(nèi)存大小是否固定,決定其屬于定長(zhǎng)對(duì)象還是變長(zhǎng)對(duì)象。相應(yīng)的需要具有頭部信息PyObject
或PyVarObject
。
因此,頭文件準(zhǔn)備了兩個(gè)頭部信息的宏定義PyObject_HEAD
和PyObject_VAR_HEAD
,方便對(duì)象使用,
#define PyObject_HEAD PyObject ob_base; #define PyObject_VAR_HEAD PyVarObject ob_base;
宏定義說(shuō)明,
#define PyObject_HEAD PyObject ob_base; 表示將代碼中其他出現(xiàn)PyObject_HEAD的地方,替換成PyObject ob_base;
1.4.1 定長(zhǎng)對(duì)象實(shí)現(xiàn)
內(nèi)存大小固定的浮點(diǎn)數(shù)類的實(shí)現(xiàn)只需在PyObject
頭部基礎(chǔ)上,用一個(gè)雙精度浮點(diǎn)數(shù)double加以實(shí)現(xiàn),
typedef struct { PyObject_HEAD double ob_fval; } PyFloatObject;
1.4.2 變長(zhǎng)對(duì)象實(shí)現(xiàn)
內(nèi)存大小不固定的列表對(duì)象則需要在PyVarObject
頭部的基礎(chǔ)上,用一個(gè)動(dòng)態(tài)數(shù)組加以實(shí)現(xiàn),數(shù)組存儲(chǔ)列表包含的對(duì)象,即 PyObject 指針,
typedef struct { PyObject_VAR_HEAD PyObject **ob_item; Py_ssize_t allocated; } PyListObject;
PyListObject底層由一個(gè)數(shù)組實(shí)現(xiàn),關(guān)鍵字段是以下3個(gè),
字段 | 說(shuō)明 |
---|---|
ob_item | 指向動(dòng)態(tài)數(shù)組的指針,數(shù)組保存元素對(duì)象指針。 |
allocated | 動(dòng)態(tài)數(shù)組總長(zhǎng)度,即列表當(dāng)前的 容量。 |
ob_size | 當(dāng)前元素個(gè)數(shù),即列表當(dāng)前的 長(zhǎng)度。 |
列表容量不足時(shí),Python會(huì)自動(dòng)擴(kuò)容,具體機(jī)制見(jiàn)list源碼解讀。
1.4.3 頭部信息宏初始化
PyObject_HEAD_INIT
用于定長(zhǎng)對(duì)象頭部信息初始化。將引用計(jì)數(shù)ob_refcnt
設(shè)置為1并將對(duì)象類型ob_type
設(shè)置成給定類型。
#define PyObject_HEAD_INIT(type) \ { _PyObject_EXTRA_INIT \ 1, type },
PyVarObject_HEAD_INIT
用于變長(zhǎng)對(duì)象頭部信息初始化。在前者基礎(chǔ)上進(jìn)一步設(shè)置長(zhǎng)度字段ob_size
。
#define PyVarObject_HEAD_INIT(type, size) \ { PyObject_HEAD_INIT(type) size },
在源碼中經(jīng)常見(jiàn)到這兩個(gè)宏定義。
二. 類型對(duì)象的基石—PyTypeObject 2.1 PyTypeObject包含信息
PyObject
記錄了Python中所有對(duì)象共有的信息。如引用計(jì)數(shù)、類型指針和變長(zhǎng)對(duì)象特有的元素個(gè)數(shù)。但是還有一些細(xì)節(jié)需要考慮,
- 創(chuàng)建不同類型的對(duì)象時(shí)如何得知對(duì)象所需的內(nèi)存信息
- 給定某個(gè)對(duì)象,如何判斷它支持什么操作
這些作為對(duì)象的元信息 ,應(yīng)該由一個(gè)獨(dú)立實(shí)體保存,與對(duì)象所屬類型密切相關(guān)。PyObject
中包含的ob_type
指針,指向一個(gè)類型對(duì)象。類型對(duì)象PyTypeObject
也在Include/object.h
中定義,關(guān)鍵字段如下,
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;
類型對(duì)象PyTypeObject
是一個(gè)變長(zhǎng)對(duì)象,包含變長(zhǎng)對(duì)象頭部信息PyObject_VAR_HEAD
和專有字段,
字段 | 說(shuō)明 |
---|---|
類型名稱 | tp_name字段 |
類型的繼承信息 | tp_base字段指向基類對(duì)象 |
創(chuàng)建實(shí)例對(duì)象時(shí)所需的內(nèi)存信息 | tp_basicsize 和 tp_itemsize 字段 |
該類型支持的相關(guān)操作信息 | tp_print、tp_getattr等函數(shù)指針 |
PyTypeObject
就是類型對(duì)象在 Python 中的表現(xiàn)形式,對(duì)應(yīng)著面向?qū)ο笾小邦悺钡母拍睢?code>PyTypeObject結(jié)構(gòu)很復(fù)雜,目前只需要知道它保存著對(duì)象的元信息,描述對(duì)象的類型即可。
2.2 類型對(duì)象和實(shí)例對(duì)象在內(nèi)存中的關(guān)系
以float為例,考察類型對(duì)象和實(shí)例對(duì)象在內(nèi)存中的形態(tài)和關(guān)系,
>>> float <class 'float'> >>> pi = 3.14 >>> e = 2.71 >>> type(pi) is float True
- 兩個(gè)float實(shí)例對(duì)象都是
PyFloatObject
結(jié)構(gòu)體,除了公共頭部字段ob_refcnt
和ob_type
,專有字段ob_fval
保存了對(duì)應(yīng)的數(shù)值。 - 類型對(duì)象是一個(gè)
PyTypeObject
結(jié)構(gòu)體,保存了類型名、內(nèi)存分配信息以及浮點(diǎn)數(shù)相關(guān)操作。實(shí)例對(duì)象的ob_type
字段指向類型對(duì)象,Python 據(jù)此判斷對(duì)象類型,進(jìn)而獲悉關(guān)于對(duì)象的元信息。 - float、pi以及e等變量只是一個(gè)指向?qū)嶋H對(duì)象的指針。
上圖的內(nèi)容并不完全正確,更深入的解讀見(jiàn)后一篇博文。
到此這篇關(guān)于Python源碼學(xué)習(xí)之PyObject和PyTypeObject的文章就介紹到這了,更多相關(guān)PyObject和PyTypeObject內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- python中class(object)的含義是什么以及用法
- Python?TypeError:?‘float‘?object?is?not?subscriptable錯(cuò)誤解決
- 最新整理Python中的type和object的示例詳解
- Python開(kāi)發(fā)時(shí)報(bào)TypeError:?‘int‘?object?is?not?iterable錯(cuò)誤的解決方式
- Python源碼學(xué)習(xí)之PyType_Type和PyBaseObject_Type詳解
- python源碼剖析之PyObject詳解
- 關(guān)于Python中object類特殊方法的解釋
相關(guān)文章
使用Python將語(yǔ)音轉(zhuǎn)換為文本的方法
這篇文章主要介紹了如何使用Python將語(yǔ)音轉(zhuǎn)換為文本,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08python django 增刪改查操作 數(shù)據(jù)庫(kù)Mysql
下面小編就為大家?guī)?lái)一篇python django 增刪改查操作 數(shù)據(jù)庫(kù)Mysql。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07python斯皮爾曼spearman相關(guān)性分析實(shí)例
這篇文章主要為大家介紹了python斯皮爾曼spearman相關(guān)性分析實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02matplotlib畫混淆矩陣與正確率曲線的實(shí)例代碼
混淆矩陣也稱誤差矩陣,是表示精度評(píng)價(jià)的一種標(biāo)準(zhǔn)格式,下面這篇文章主要給大家介紹了關(guān)于matplotlib畫混淆矩陣與正確率曲線的相關(guān)資料,需要的朋友可以參考下2021-06-06詳解如何在pyqt中通過(guò)OpenCV實(shí)現(xiàn)對(duì)窗口的透視變換
這篇文章主要介紹了如何在pyqt中通過(guò)OpenCV實(shí)現(xiàn)對(duì)窗口的透視變換,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09pandas進(jìn)行數(shù)據(jù)的交集與并集方式的數(shù)據(jù)合并方法
今天小編就為大家分享一篇pandas進(jìn)行數(shù)據(jù)的交集與并集方式的數(shù)據(jù)合并方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-06-06在pycharm中關(guān)掉ipython console/PyDev操作
這篇文章主要介紹了在pycharm中關(guān)掉ipython console/PyDev操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-06-06