前言,這本書比較早,源碼剖析是針對python2.5python
python架構主要分爲三部分c++
- 從左到右分別爲,python文件,python解釋器,運行時環境
- python解釋器:
- scanner 詞法分析,將代碼且分爲一個個token
- parser語法分析創建AST
- compiler根據AST生成python 字節碼
- code Evaluator(虛擬機)執行這些字節碼
- 運行時環境:
- 內建對象:list,dict等
- 內存分配器:和malloc的一層接口
- 運行狀態信息: 維護解釋器在執行字節碼時的不一樣狀態(還不知道具體是怎樣的)
- include: python 提供的全部頭文件,用於用戶經過c或c++來編寫自定義模塊
- Lib: 包含全部python自帶標準庫,用python編寫的
- Modules: c編寫的模塊
- parser: 解釋器Scanner和Parser部分
- Objects: 內建對象,list, dict,整數等等
- Python: Compiler和執行引擎部分
一些tips: api中以NEW結尾當作c++ new, Malloc結尾的當作c mallorapi
python中類,對象,都是經過對象來實現的數組
- 人的視角: 對象是數據和基於這些數據操做的集合
- 計算機視角: 對象是一片被分配的內存空間(連續或離散),能夠在更高層次上做爲一個總體來考慮,這個總體就是對象
- python 全部內建類型對象是靜態初始化的
- python 一個對象一旦被建立,它內存中的大小就是不可變的(經過指針來定向變長數據)
// object.h
#define PyObject_HEAD \ _PyObject_HEAD_EXTRA \ // 通常狀況下爲空
int ob_refcnt; // 引用計數
struct _typeobject *ob_type; //指定一個對象類型的類型對象
typedef struct _object{
PyObject_HEAD
} PyObject;
複製代碼
- 每個python對象除了PyObject以外還須要額外的內存,PyObject定義了必需要的數據好比PyIntObject
// intobject.h
typedef struct _object{
PyObject_HEAD
long ob_ival;
} PyIntObject;
複製代碼
- 對於變長對象python有新的抽象
// object.h
#define PyObject_VAR_HEAD \ PyObject_HEAD \ long ob_size; /通常指容器內元素數量
typedef struct _object{
PyObject_VAR_HEAD
} PyVarObject;
複製代碼
PyObject佔用內存大小是對象的元信息,元信息和對象所屬類型密切相關(下一節介紹)架構
// object.h
typedef struct _typeobject{
PyObject_VAR_HEAD
char *tp_name; //print 信息"<module>.<name>"
int tp_basicsize, item_size; //爲了分配內存大小
destructor tp_dealloc;
printfunc tp_print;
hashfunc tp_hash;
ternaryfunc tp_call;
...
} PyTypeObject;
複製代碼
python對象的建立主要有兩種方式less
- C API(對於python的內建對象, 直接分配內存)
- 泛型API AOL
- 類型相關API COL
- 類型對象建立(用戶自定義對象,由於不可能事先提供這類C方法),大體流程以下
- 會調用ob_type 所指定的PyTyoeObject類的 tp_new方法,若是tp_new是NULL會追溯tp_base所指向的ob_type的tp_new方法,最終定位的tp_new(由於全部類繼承object會有保底的tp_new方法)負責內存的申請(相似c++的new)
- 以後經過tp_init初始化(相似c++的構造函數)
PyTypeObect 實際也是有ob_type屬性的,其爲PyType_Type(即type, 負責PyTypeObect的建立,即metaclass)函數
下圖以int 爲例說明了這些關係,也就是所知的python裏面class,object,type的關係 this
其餘略lua
主要是PyIntObject和PyInt_Type,其餘和廣泛的PyObject, PyTyoeObject沒什麼,值得關注的有spa
- python2中稍小一點的數直接用C語言中的long去存儲,稍大一點的數(超過long的承受範圍)會使用python的long對象去存儲,而python3不會做區分,統一用longObect去存儲,實現用到了柔性數組,感興趣能夠查一下
- 小整形數組的內存池和大整形對象的內存鏈的維護,避免頻繁malloc,後面主要介紹下
使用了之前分配好的對象池
// [intobject.c]
#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS 257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS 5
#endif
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
/* References to small integers are saved in this array so that they can be shared. The integers that are saved are those in the range -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive). */
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
#endif
複製代碼
對於其餘整數,Python運行環境將會提供一塊內存空間,這塊內存空間由這些對象輪流使用,避免了頻繁malloc。 在Python經過PyIntBlock結構,實現這個機制。
// [intobject.c]
#define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */
#define BHEAD_SIZE 8 /* Enough for a 64-bit pointer */
#define N_INTOBJECTS ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject))
struct _intblock {
struct _intblock *next;
PyIntObject objects[N_INTOBJECTS];
};
typedef struct _intblock PyIntBlock;
static PyIntBlock *block_list = NULL;
static PyIntObject *free_list = NULL;
複製代碼
能夠建立int的代碼理解這兩塊的使用
// [intobject.c]
PyObject * PyInt_FromLong(long ival) {
register PyIntObject *v;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0 /* 嘗試使用小整數對象池 */
if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
v = small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
return (PyObject *) v;
}
#endif
if (free_list == NULL) {
if ((free_list = fill_free_list()) == NULL)
return NULL;
}
/* Inline PyObject_New */
v = free_list;
free_list = (PyIntObject *)v->ob_type; /* 有一個看似不合適可是比較方便的地方,freelist會經過 ob_type存放可用空間的pyObject的地址(相似鏈表的next),而不是 PyTyoeObject */
(void)PyObject_INIT(v, &PyInt_Type);
v->ob_ival = ival;
return (PyObject *) v;
}
複製代碼
下面是關於freelist的申請,和freelist和block_list的維護有關的代碼
// [intobject.c]
static PyIntObject * fill_free_list(void) {
PyIntObject *p, *q;
/* 申請大小爲sizeof(PyIntBlock)的內存空間,並連接到已有的block list中 */
p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));
((PyIntBlock *)p)->next = block_list;
block_list = (PyIntBlock *)p;
/* 將PyIntBlock中的PyIntObject數組——objects轉變成單向鏈表*/
p = &((PyIntBlock *)p)->objects[0];
q = p + N_INTOBJECTS;
while (--q > p)
q->ob_type = (struct _typeobject *)(q-1); /* 上一段代碼中所提到的不合適的地方 Py_TYPE(q) = NULL; return p + N_INTOBJECTS - 1; } 複製代碼
這樣,freelist會指向能夠分配內存的地址,可是若是由以前分配的PyIntObject被釋放了,freelist須要將被釋放的地址從新使用才能夠,這個是經過PyIntObect的析構函數來實現的
// [intobject.c]
static void int_dealloc(PyIntObject *v) {
if (PyInt_CheckExact(v)) { // 若是不是派生類這麼執行,保證freelist的完整性
v->ob_type = (struct _typeobject *)free_list;
free_list = v;
}
else // 若是是派生類,則執行正常的析構流程
v->ob_type->tp_free((PyObject *)v);
}
複製代碼