我認識的python(2)

渣渣強發表於2018-07-02

python原理

這篇文章主要是介紹python一些基本的原理。 python的整體架構可以分為三部分,分別是模組庫管理,直譯器還有執行時環境(包括物件/型別系統,記憶體分配器和執行時狀態資訊)。

模組庫管理主要是python提供的大量模組,庫以及使用者自定義的python模組程式碼。

直譯器是python程式碼執行的核心元件,首先直譯器對檔案進行Scanner詞法分析,將一行行程式碼轉換成token流,接下來Parser過程對應語法分析,針對Scanner的結果進行語法分析,生成AST語法樹,Compile針對AST語法樹的結果生成指令集合-位元組碼。最後code evaluator對位元組碼進行執行。

執行時環境包括了物件/型別系統,記憶體分配器和執行時狀態資訊。執行時狀態資訊是維護直譯器在執行指令的不同狀態之間切換的動作,類似一個有窮的狀態機。記憶體分配器是用於管理python物件的建立,對記憶體的申請,針對malloc進行一層封裝。物件/型別系統包含python內建的各種資料結構物件和使用者自定義的各種型別和物件。

python整體結構

python物件/型別系統

python的世界裡任何東西都屬於物件,即使型別也是物件的一員。 一般有常見的內建型別物件int,string,list等還有自己定義型別物件。針對這些型別,我們可以進行物件例項化。物件之間包含著很多複雜的關係。

python物件對應就是c在堆中分配的空間。而python的內建型別物件對應的空間是在靜態初始化。物件一旦分配好了,空間就已經固定了,所以要想可變長,必須維護一個指向其他可變空間的地址空間的指標。

python object 對應c語言的表達

typedef struct _object {
    PyObject_HEAD
} PyObject;

#define PyObject_HEAD \
    _PyObject_HEAD_EXTRA\
    int obj_refcnt;
    struct _typeobject *obj_type;
複製程式碼

obj_refcnt對應是引用計數,obj_type對應的是型別。
變長物件表達:

typedef struct {
    PyObject_HEAD
    long ob_lval;
}

#define PyObject_VAR_HEAD\
    PyObject_HEAD\
    int ob_size;

typedef struct {
    PyObject_VAR_HEAD
}  PyVarObject;
複製程式碼

型別物件

在記憶體中分配空間,建立物件時,毫無疑問,我們需要指導物件佔用的空間是多大,不同的物件佔用的空間大小是不一樣的。佔用記憶體空間的大小這個元資訊與物件的型別是緊密關係在一起,它會出現在物件型別之中。

typedef struct _typeobject {
    PyObject_VAR_HEAD
    char * tp_name;
    int tp_basicsize; 建立該型別分配的記憶體空間
    
    destructor tp_dealloc;
    printfunc tp_prinf;
    ...
    hashfunc tp_hash;
    ternaryfunc tp_call;
    ...
} PyTypeObject
複製程式碼

物件的建立方式

python物件的建立有兩種方式:

  1. 通過c api建立,如PyObject obj = PyObject_New(PyObject, &PyInt_Type)
  2. 另外一種是與物件相關的api,只能作用於某種型別的物件上,如PyObject *intobj = PyInt_Fromlong(10)

我認識的python(2)
tp_base 表明繼承關係,tp_new則是逐層查詢非null定義並根據tp_basicsize生成物件記憶體空間。tp_init則為初始化階段。

物件常用的函式簇

PyTypeObject 中有三個常用的函式簇,針對數字,列表,字典型別的函式集,分別是PyNumberMethods, PySequenceMethods, PyMappingMethods.

typedef PyObject* (*binaryfunc)(PyObject* , PyObject*);

typedef struct {
    binaryfunc nb_add;
    binaryfunc nb_substract;
    ....
} PyNumberMethods;
複製程式碼

tp_as_number.nb_add定義了python物件+操作符的執行動作。

python的PyTypeObject的型別是什麼

PyTypeObject PyType_Type = {
    PyObject_HEAD_INIT(&PyType_Type)
    0,
    "type",
    sizeof(PyHeapTypeObject),  /* tp_basicsize */
    sizeof(PyMemberDef),   /* tp_itemsize */
    ....
}
複製程式碼

那麼可以整理出這樣的一個層次關係。
PyObject => PyTypeObject => PyType_Type(這個就是常說的metaclass) 對於不同內建型別的物件有著自己的PyType_Type.譬如int型別

PyTypeObject PyInt_Type = {
    PyObject_HEAD_INIT(&PyType_Type)
    0,
    "int",
    sizeof(PyIntObject),
    ....
}
複製程式碼

我認識的python(2)
a = ObjectB() 首先會生成一個PyTypeObject的ObjectB,然後a的ob_type指向該物件。

多型性的體現

物件的多型性來源於ob_type域,比如

void Print(PyObject* object){
    object->ob_type->tp_print(object);
}
複製程式碼

垃圾回收

我認識的python(2)
Py_Dealloc過程並不會真正去做釋放記憶體的動作,而是採用記憶體物件池技術,通過這個技術可以避免記憶體頻繁的申請和釋放。

物件的分類

萬物皆物件。

我認識的python(2)

相關文章