python 源碼剖析0~2章

準備工做

前言,這本書比較早,源碼剖析是針對python2.5python

1.python的整體架構

python架構主要分爲三部分c++

  • 從左到右分別爲,python文件,python解釋器,運行時環境
    • python解釋器:
      • scanner 詞法分析,將代碼且分爲一個個token
      • parser語法分析創建AST
      • compiler根據AST生成python 字節碼
      • code Evaluator(虛擬機)執行這些字節碼
    • 運行時環境:
      • 內建對象:list,dict等
      • 內存分配器:和malloc的一層接口
      • 運行狀態信息: 維護解釋器在執行字節碼時的不一樣狀態(還不知道具體是怎樣的)

2.python源碼的組織

  • 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中類,對象,都是經過對象來實現的數組

1.python內的對象

  • 人的視角: 對象是數據和基於這些數據操做的集合
  • 計算機視角: 對象是一片被分配的內存空間(連續或離散),能夠在更高層次上做爲一個總體來考慮,這個總體就是對象
  • python 全部內建類型對象是靜態初始化的
  • python 一個對象一旦被建立,它內存中的大小就是不可變的(經過指針來定向變長數據)

1.1 PyObject

// 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;
複製代碼

1.2類型對象

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;
複製代碼

1 對象的建立

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++的構造函數)

2 類型的類型

PyTypeObect 實際也是有ob_type屬性的,其爲PyType_Type(即type, 負責PyTypeObect的建立,即metaclass)函數

下圖以int 爲例說明了這些關係,也就是所知的python裏面class,object,type的關係 this

其餘略lua

二.python中的整數對象

主要是PyIntObject和PyInt_Type,其餘和廣泛的PyObject, PyTyoeObject沒什麼,值得關注的有spa

  • python2中稍小一點的數直接用C語言中的long去存儲,稍大一點的數(超過long的承受範圍)會使用python的long對象去存儲,而python3不會做區分,統一用longObect去存儲,實現用到了柔性數組,感興趣能夠查一下
  • 小整形數組的內存池和大整形對象的內存鏈的維護,避免頻繁malloc,後面主要介紹下

2.1 小整形對象

使用了之前分配好的對象池

// [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
複製代碼

2.2 大整形對象

對於其餘整數,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);
}
複製代碼
相關文章
相關標籤/搜索