<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在“Python原始碼學習筆記:Python萬物皆物件”中,我們對Python的物件型別體系有了一定的認識,這篇部落格將從原始碼層面來介紹Python中萬物皆物件的底層實現。
在Python直譯器的C層面,一切物件都是以PyObject為基礎的
C原始碼如下:
typedef struct _object { _PyObject_HEAD_EXTRA Py_ssize_t ob_refcnt; PyTypeObject *ob_type; } PyObject;
原始碼解讀:
_PyObject_HEAD_EXTRA:主要用於實現雙向連結串列(分析原始碼時暫時忽略)
ob_refcnt:參照計數,用於垃圾回收機制,當這個引數減少為0時即代表物件要被刪除了(Py_ssize_t當作int或long即可,感興趣的話可以去看下它的定義)
ob_type:型別指標,指向物件的型別物件(PyTypeObject,稍後介紹),型別物件描述範例物件的資料及行為。如PyLongObject的ob_type指向的就是PyLong_Type
PyVarObject與PyObject相比只多了一個屬性ob_size,它指明瞭邊長物件中有多少個元素
C原始碼如下:
typedef struct { PyObject ob_base; Py_ssize_t ob_size; /* Number of items in variable part */ } PyVarObject;
定長物件和變長物件的大致結構圖示如下:
宏定義:對於具體物件,視其大小是否固定,需要包含頭部PyObject或PyVarObject,為此,標頭檔案准備了兩個宏定義,方便其他物件使用:
#define PyObject_HEAD PyObject ob_base; #define PyObject_VAR_HEAD PyVarObject ob_base;
這裡簡單的以浮點物件作為定長物件的例子,介紹一下相關概念,後續會詳細分析float物件的原始碼。
對於大小固定的浮點物件,需要在PyObject頭部的基礎上,用一個雙精度浮點數double加以實現:
typedef struct { PyObject_HEAD double ob_fval; } PyFloatObject;
圖示如下:
這裡簡單的以列表物件作為變長物件的例子,介紹一下相關概念,後續會詳細分析list物件的原始碼。
對於大小不固定的列表物件,需要在PyVarObject頭部的基礎上,用一個動態陣列加以實現,陣列儲存了列表包含的物件的指標,即PyObject指標:
typedef struct { PyObject_VAR_HEAD PyObject **ob_item; Py_ssize_t allocated; } PyListObject;
原始碼解讀:
ob_item:指向動態陣列的指標,陣列中儲存元素物件指標
allocated:動態陣列的總長度,即列表當前的“容量”
ob_size:當前元素個數,即列表當前的長度(這裡的長度是指:列表包含n個元素,則長度為n)
圖示如下:
問題:不同型別的物件所需儲存空間不同,建立物件時從哪得知儲存資訊呢?以及如何判斷一個給定物件支援哪些操作呢?
注意到,PyObject結構體中包含一個指標ob_type,指向的就是型別物件,其中就包含了上述問題所需要的資訊
C原始碼如下:(只列出了部分,後續會結合具體型別進行分析)
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 */ PyObject *tp_bases; // ... } PyTypeObject;
原始碼解讀:
PyObject_VAR_HEAD表示PyTypeObject是變長物件
tp_name:型別名稱
tp_basicsize、tp_itemsize:建立範例物件時所需的記憶體資訊
tp_print、tp_getattr等:表示該型別支援的相關操作資訊
tp_bases:指向基礎類別物件,表示型別的繼承資訊
PyTypeObject就是型別物件在C層面的表示形式,對應物件導向中”類“的概念,其中儲存著物件的”元資訊“(即一類物件的操作、資料等)。
下面以浮點型別為例,列出了PyFloatObject和PyTypeObject之間的關係結構圖示:(其中兩個浮點範例物件都是PyFloatObject結構體,浮點型別物件float是一個PyTypeObject結構體變數)
由於浮點型別物件唯一,在C語言層面作為一個全域性變數靜態定義即可。C原始碼如下:(只列出了部分)
PyTypeObject PyFloat_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "float", sizeof(PyFloatObject), 0, (destructor)float_dealloc, /* tp_dealloc */ // ... (reprfunc)float_repr, /* tp_repr */ // ... };
原始碼解讀:
第二行PyVarObject_HEAD_INIT(&PyType_Type, 0):初始化了ob_refcnt、ob_type、ob_sie三個欄位,其中ob_type指向了PyType_Type(稍後會繼續介紹,它就是type),即:float的型別是type
第三行"float":將tp_name欄位初始化為型別名稱float
通過PyFloat_Type的ob_type欄位,我們找到了type所對應的C語言層面結構體變數:PyType_Type,C原始碼如下:(只列出了部分)
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 */ // ... (reprfunc)type_repr, /* tp_repr */ // ... };
內建型別和自定義類對應的PyTypeObject物件都是通過這個PyType_Type建立的。在第二行PyVarObject_HEAD_INIT(&PyType_Type, 0)中,PyType_Type把自己的ob_type欄位設定成了它自己,即type的型別是type
把PyType_Type加入到結構圖中,圖示如下:
object是另外一個特殊的型別,它是所有型別的基礎類別。如果要找到object對應的結構體,我們可以通過PyFloat_Type的tp_base欄位來尋找,因為它指向的就是float的基礎類別object。但是我們檢視原始碼發現,PyFloat_Type中並沒有初始化tp_base欄位:
同樣地,我們檢視Objects資料夾下的各種不同型別所對應的結構體,發現tp_base欄位均沒有初始化,於是尋找將tp_base欄位初始化的函數:
void _Py_ReadyTypes(void) { if (PyType_Ready(&PyBaseObject_Type) < 0) Py_FatalError("Can't initialize object type"); if (PyType_Ready(&PyType_Type) < 0) Py_FatalError("Can't initialize type type"); // ... if (PyType_Ready(&PyFloat_Type) < 0) Py_FatalError("Can't initialize float type"); // ... }
_Py_ReadyTypes中統一呼叫了PyType_Ready()函數,為各種型別設定tp_base欄位:
int PyType_Ready(PyTypeObject *type) { // ... /* Initialize tp_base (defaults to BaseObject unless that's us) */ base = type->tp_base; if (base == NULL && type != &PyBaseObject_Type) { base = type->tp_base = &PyBaseObject_Type; Py_INCREF(base); } // ... }
可以看到,PyType_Ready在初始化tp_base欄位時,對於PyBaseObject_Type,不會設定tp_base欄位,即object是沒有基礎類別的,這就是為了保證繼承鏈有一個終點。
PyBaseObject_Type原始碼如下:(只列出了部分)
PyTypeObject PyBaseObject_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "object", /* tp_name */ sizeof(PyObject), /* tp_basicsize */ 0, /* tp_itemsize */ object_dealloc, /* tp_dealloc */ // ... object_repr, /* tp_repr */ // ... 0, /* tp_base */ // ... };
原始碼解讀:
第二行PyVarObject_HEAD_INIT(&PyType_Type, 0):把ob_type設定為PyType_Type,即object的型別是type
將PyBaseObject_Type加入到結構圖中,圖示如下:
object的型別是type,type的基礎類別是object。先有雞還是先有蛋?
答:
前面我們提到,在各種型別對應的C語言結構體變數初始化的時候,tp_base欄位都是沒有設定具體值的,直到_Py_ReadyTypes()函數執行時,才通過PyType_Ready()去初始化各型別的tp_base。
在PyBaseObject_Type初始化時,會將ob_tyep欄位設定為PyType_Type,即object的型別為type;在_Py_ReadyTypes函數中,會通過PyType_Ready()設定PyType_Type的tp_base欄位為PyBaseObject_Type。所以這裡本質上不是一個先有雞還是先有蛋的問題。
PyTypeObject儲存元資訊:某種型別的範例物件所共有的資訊儲存在型別物件中,範例物件所特有的資訊儲存在範例物件中。以float為例:
以上就是Python物件的底層實現原始碼學習的詳細內容,更多關於Python物件底層的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45