1. 程式人生 > >《Python源碼剖析》

《Python源碼剖析》

相關 ref 成就 疑問 流程 指定 這一 數值 出現

1. Python內建對象

對象是數據以及基於這些數據的操作的集合,在Python中,對象就是為C中的結構體在堆上申請的一塊內存。

在Python中,一個對象一旦被創建,它在內存中的大小就是不變的了,那些需要容納可變長度數據的對象只能在對象內維護一個指向一塊可變大小的內存區域的指針。

1.1.1 對象機制的基石 -- PyObject

[object.h]
typedef struct _object{
    int ob_refcnt;                        /* 引用計數器 */
    struct _typeobject *ob_type;          /* _typeobject這個結構體用來指定一個對象類型的類型對象 */
    } PyObject
    

在Python中,對象機制的核心非常簡單,一個是引用計數,一個是類型信息

在PyObject中定義了每一個Python對象都必須有的內容,除了PyObject以外,還應該有一些額外的內存,放置其他信息。

1.1.2 定長對象和變長對象

Python整數對象PyIntObject
typedef struct{
    PyObject_HEAD
    long ob_ival;
    } PyIntObject

整數對象的特殊信息是一個C中的整形變量,無論這個整數對象的值有多大,都可以保存在這個整形變量中。

變長對象PyVarObject
#define PyObject_VAR_HEAD     PyObject_HEAD             int ob_size;                           /* 容器中元素的數量 */
    
typedef struct{
    PyObject_VAR_HEAD
    } PyVarObject;
    

我們把整形對象這樣不包含可變長度數據的對象稱為“定長對象”,而字符串對象這樣包含可變長度數據的對象稱為“變長對象”,它們的區別在於定長對象的不同對象占用內存大小是一樣的,而變長對象的不同對象占用內存可能不一樣。

PyVarObject只是對PyObject的一個擴展而已,在Python內部,每一個對象都擁有相同的對象頭部。

1.2 類型對象

創建對象的時候,必須知道要申請多大的空間,所需空間大小是對象的一種元信息,這樣的元信息與對象所屬類型密切相關。我們考察一下類型對象_typeobject:

typedef struct _typeobject {
    PyObject_VAR_HEAD
    char *tp_name;                          /* tp_name 類型名 */
    int tp_basicsize, tp_itemsize;          /* 創建該類型對象時分配內存空間的大小 */
    
    /* 與對象關聯的操作信息 */
    destructor tp_dealloc;
    printfunc tp_print;
    
    /* 更多操作信息 */ 
    hashfunc tp_hash;
    ternaryfunc tp_call;
    ...
    } PyTypeObject;
    

一個PyTypeObject就是Python中面向對象理論中“類”這個概念的實現

1.2.1 對象的創建

Python創建對象的方法:

  1. 通過Python C API來創建
  2. 通過類型對象 PyInt_Type來創建

Python對外提供了C API讓用戶可以從C環境中與Python交互,Python的C API分為兩類:

  1. 範型API,或稱為AOL
  2. 另一類稱為COL

從PyInt_Type創建整數對象

說明: 在Python完成運行環境的初始化後,符號‘int‘就對應著Python內部的PyInt_Type對象,object則對應著PyBaseObject_Type對象

  1. 使用init(10)創建對象時,就會調用PyInt_Type對象中的tp_new操作
  2. 假如這一個tp_new操作不存在,為NULL,那麽就到tp_base指定的基類裏去找tp_new操作
  3. 在Python2.2以後的新式類中,所有的類都是以object為基類的,所以最終一定可以找到一個不為NULL的tp_new
  4. tp_new指向了object_new,object_new會訪問PyInt_Type中記錄的tp_basicsize信息,得到需要申請的內存大小,然後完成申請內存的操作。我們前面說了,“在Python中,對象就是為C中的結構體在堆上申請的一塊內存”,所以內存申請完成就代表對象已經創建(但還未初始化)
  5. tp_new創建完對象後,流程轉向PyInt_Type的tp_init,完成初始化工作

當我們用int(10)創建整數對象10時,首先PyInt_Type中的tp_new會被調用,如果這個tp_new為NULL,那麽會到tp_base指定的基類中去尋找tp_new操作

1.2.2 對象的行為

在PyTypeObject中定義了大量的函數指針,這些函數都最終會指向某個函數,或者指向NULL,這些函數指針可以視為類型對象中所定義的操作,而這些操作直接決定著一個對象在運行時所表現出的行為。

比如PyTypeObject中的tp_hash是一個函數指針(hashfunc)類型的變量,tp_hash指明對於該類型的對象如何生成其hash值。
在PyTypeObject中指定的不同的操作信息也正是一種對象區別於另一種對象的關鍵所在。

在這些操作族中,有三組非常重要:

tp_as_number
指向PyNumberMethods函數族,該族定義了一個數值對象應該支持的操作,典型對象如int,tp_as_number.nb_add指定了
對該對象進行加法操作時的具體行為。
tp_as_sequence
指向PySequenceMethods函數族,該族定義了作為一個序列對象應該支持的操作,典型對象list.
tp_as_mapping
指向PyMappingMethods函數族,該族定義了作為一個關聯對象應該支持的操作,典型對象dict。

對一種類型來說,它完全可以同時定義三個函數族中的所有操作,只要定義相應的special_method

1.2.3 類型的類型

在Python中類型其實也是一個對象,類型的類對應Python內部的PyType_Type,它是所有class的class,在Python中稱為metaclass,元類。

此處留疑問??????????????

[object.h]
#ifdef Py_TRACE_REFS
    #define _PyObject_EXTRA_INIT 0,0,
#else
    #define _PyObject_EXTRA_INIT
#endif

#define PyObject_HEAD_INIT(type)    _PyObject_EXTRA_INIT 1, type

番外篇: #define學習

\反斜杠把該定義延續到下一行,編譯器將\分成的多個物理行轉換為一個邏輯行,並刪除\符號

#define預處理器指令以#號作為一行的開始,指令可以出現在源文件的任何地方,其定義從指令出現的地方到該文件末尾有效。
每行#define(邏輯行)都由3部分組成:

  1. 第1部分是#define指令本身
  2. 第2部分是選定的縮寫,也稱為,有些宏代表值,這些宏被稱為類對象宏
  3. 第3部分稱為替換列表或替換體

從宏變成最終替換文本的過程稱為宏展開
在#define中使用參數可以創建外形和作用與函數類似的類函數宏

《Python源碼剖析》