標籤:ems app 記憶體 ref into 個數 記錄 type string類
在python中一切皆是對象,那麼這種機制是如何?的呢?下面就讓我們從python的老巢“源碼”來看看這個神秘的機制。
typedef struct _object {
PyObject_HEAD
} PyObject;
typedef struct {
PyObject_VAR_HEAD
} PyVarObject;
看到上面這兩個結構體了嗎?它們就是萬源之源,所有python對象的祖先,為什麼有兩個祖先呢?這就要從現即時間的資料對象談起,我們有些資料,像數字、固定矩陣這類資料其元素個數是固定不變的,而像字串、集合、字典這類資料其元素個數是會上下浮動的。這樣python的發明者在設計python時就為這兩類資料創造了兩個祖先(像人有男人與女人一樣,好像不太確切,但本質就是有兩個祖先了):定長資料類型與變長資料類型。
這兩個結構體裡面的兩個變數,其實都是些宏。從源中我們可以找到這些宏的展開:
#define PyObject_HEAD \
_PyObject_HEAD_EXTRA \
Py_ssize_t ob_refcnt; \
struct _typeobject *ob_type;
#define PyObject_VAR_HEAD \
PyObject_HEAD \
Py_ssize_t ob_size;
看得出來,類型多了的這個ob_size就說明了它的變長特性(元素的個數)。
下面我們來仔細看看PyObject_HEAD這個宏的內容:
_PyObject_HEAD_EXTRA:這個宏展開時要麼是雙向鏈表的前後指標要麼是空,是在跟蹤所有對象時使用:想像一下,一個雙向鏈表把python所有的對象串聯起來的情境,好宏大呀!
ob_refcnt:這個屬性記錄該對象被他人引用的次數(後面發現,這個東西是用來進行記憶體管理的:引用計數式的記憶體回收機制)
ob_type:這個玩意是個重頭戲,它實現了python的多態(何謂多態:對於不同的類型的對象,相同名稱的方法其表現行為不同),何以見得呢?下面就讓我們重點分析下這個ob_type,首先看看ob_type的原型是什麼:
typedef struct _typeobject {
PyObject_VAR_HEAD
const char *tp_name;
Py_ssize_t tp_basicsize, tp_itemsize;
printfunc tp_print;
。。。
PyNumberMethods *tp_as_number;
PySequenceMethods *tp_as_sequence;
PyMappingMethods *tp_as_mapping;
。。。
} PyTypeObject;
這個結構體在原始碼中整整佔了84行,天呢!它是個什麼,這麼多內容。我們重點看幾個欄位:
PyObject_VAR_HEAD
:嗯,這不是變長資料類型它老祖先裡的頭‘骨’嗎?怎麼這裡還有呢?沒錯,你猜對了,我們指明某個物件類型的對象原型 PyTypeObject也是一個對象。至於為什麼是變長類型的對象祖先的頭’骨‘,還沒搞清楚^0^。
tp_print:多態的內部原理開始顯露了吧!就是通過這個type對象來為具體不同的對象提供不同的方法實現,盡量方法名字都一樣。
PyNumberMethods,PySequenceMethods,PyMappingMethods:其實和上面這個tp_print一樣,只不它們是個函數族(由函數集構成),類數字操作(比如支援+-×/)、類序列操作(比如[n:m]切片操作)、字典操作(比如dict[key]操作)。
下面就int類型對象對上面這些原理進行驗證:
typedef struct {
PyObject_HEAD
long ob_ival;
} PyIntObject;
這就是我們的int類型對象,其頭部呢?在python源中可以看出,用一系列宏來實現頭部的初始化:
#define PyObject_HEAD_INIT(type) \
_PyObject_EXTRA_INIT \
1, type,
這裡的type,從源碼中看出,有一個PyType_Type類型,它就是那個描述類型的類型的對象。有點繞了,但是仔細理解下來,也沒什麼,無外乎:一個int資料,有一個專門描述int類型資料的類型對象,但是不光有int類型的類型對象還有string類型的類型對象。那麼就需要有一個要對象來描述這些類型對象,這就是PyType_Type,也就是它可以測試一個對象是不是類型對象。
Python源碼分析:PyObject對象的起源與多態的實現