日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python源码剖析笔记

發布時間:2023/12/10 python 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python源码剖析笔记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 第0章 準備工作
    • 0.1 Python總體架構
      • 0.1.1 左邊(python提供的模塊, 庫和用戶自定義模塊)
      • 0.1.2 右邊(python的運行時環境)
      • 0.1.3 中間(python的核心--解釋器)
      • 0.1.4 右邊和中間的關系
    • 0.2 python源代碼的組織
      • 0.2.1 下載python2.5源碼
      • 0.2.2 解壓源碼后的目錄結構
        • 0.2.2.1 主要目錄說明
    • 0.3 Unix/Linux環境下編譯python
    • 0.4 修改python源代碼
      • 0.4.1 輸出python對象的接口
      • 0.4.2 重定向標準輸出
    • 0.5 注意事項
  • 第一章 python對象初探(20-06-01 *)
    • 1.1 python內的對象
      • 1.1.1 對象機制的基石——PyObject
      • 1.1.2 變長對象
    • 1.2 類型對象
      • 1.2.1 對象的創建
      • 1.2.2 對象的行為
      • 1.2.3 類型的類型
    • 1.3 Python對象的多態性
    • 1.4 引用計數
  • 第2章 python中的整數對象
    • 2.1 初識`PyIntObject`對象
    • 2.2 `PyIntObject`對象的創建和維護
      • 2.2.1 對象創建的3中途徑
      • 2.2.2 小整數對象
      • 2.2.3 大整數對象
      • 2.2.4 添加和刪除
        • 2.2.4.1 使用小整數對象池
        • 2.2.4.2 創建通用整數對象池
        • 2.2.4.3 使用通用整數對象池
  • FQA

第0章 準備工作

0.1 Python總體架構

0.1.1 左邊(python提供的模塊, 庫和用戶自定義模塊)

概念說明
模塊單個文件的形式
應該指包和模塊兩種形式(這里暫時不清楚)
自定義模塊

0.1.2 右邊(python的運行時環境)

概念說明
運行時狀態(Current State of Python)維護了解釋器在執行字節碼時不同的狀態(比如正常狀態和異常狀態)之間切換的動作. 可以看作一個有窮狀態機.
內存分配器(Memory Allocator)負責python中創建對象時, 對內存的申請工作, 實際上它就是python運行時與C中malloc的一層接口.
對象/類型系統(Object/Type Structures)包含了python中存在的各種內建對象, 比如整數, list和dict, 以及各種用戶自定義的類型和對象.

0.1.3 中間(python的核心–解釋器)

概念說明
箭頭指示了python運行過程中的數據流方向
Scanner對應詞法分析, 將文件輸入的python源代碼 或 從命令行輸入的一行行python代碼切分為一個個的token
Parser對應語法分析, 在Scanner的分析結果上進行語法分析, 建立抽象語法樹(AST)
Compiler是根據簡歷的AST生成指令集合 – python字節碼(byte code), 就像java編譯器和C#編譯器所做的那樣
Code Evaluator執行字節碼

0.1.4 右邊和中間的關系

關系說明
使用解釋器與右邊的對象/類型系統, 內存分配器之間的箭頭表示"使用"關系
修改解釋器與右邊運行狀態之間的箭頭表示"修改"關系, 即python在執行的過程中會不斷地修改當前解釋器所處的狀態, 在不同的狀態之間切換

0.2 python源代碼的組織

0.2.1 下載python2.5源碼

  • python2.5下載地址

0.2.2 解壓源碼后的目錄結構

0.2.2.1 主要目錄說明

概念說明
Lib該目錄包含了Python自帶的所有標準庫, Lib中的庫都是用Python語言編寫的
Modules該目錄中包含了所有用C語言編寫的模塊, 比如random, cStringIO等. Modules中的模塊是那些對速度要求非常嚴格的模塊,而有一些對速度沒有太嚴格要求的模塊, 比如os, 就是用python編寫,并且放在Lib目錄下的
Parser該目錄中包含了python解釋器中的Scanner和Parser部分, 即對python源代碼進行詞法分析和語法分析的部分. 除了這些, Parser目錄下還包含了一些有用的工具, 這些工具能夠根據python的語法自動生成python的詞法和語法分析器, 與YACC非常類似
Objects該目錄中包含了所有python的內建對象, 包括整數, list, dict等. 同時, 該目錄還包括了python在運行時需要的所有的內部使用對象的實現
Python該目錄下包含了python解釋器中的Compiler和執行引擎部分,成python運行的核心所在

0.3 Unix/Linux環境下編譯python

實驗環境ubuntu 18.04
查看Python-2.5/Setup.py文件中的detect_tkinter函數, 來確定需要下載的tcl和tk版本

  • 設置安裝位置
  • export LOCAL=$HOME/.local
  • 安裝tcl
  • wget -c http://prdownloads.sourceforge.net/tcl/tcl8.4.20-src.tar.gz tar xf tcl8.4.20-src.tar.gz ./unix/configure --prefix=$LOCAL make -j4 # 四核處理器 make install
  • 安裝tk
  • wget -c http://prdownloads.sourceforge.net/tcl/tk8.4.20-src.tar.gz tar xvf tk8.4.20-src.tar.gz ./unix/configure --prefix=$LOCAL make -j4 make install
  • 安裝python
  • SVNVERSION="Unversioned directory" ./configure --prefix=$LOCAL --enable-shared --with-tcltk-includes="-I$LOCAL/include" --with-tcltk-libs="-L$LOCAL/lib -ltcl8.6 -L$LOCAL/lib -ltk8.6" export LD_LIBRARY_PATH=$LOCAL/lib:$LD_LIBRARY_PATH make -j4 make install

    目錄

    目錄說明
    bin可執行文件
    lib存放的是python的標準庫; lib/python2.5/config下存放的是libpython2.5.a(用c語言對python擴展時需要用到這個靜態庫.
    include頭文件

    選項

    選項說明
    –prefixpython將被安裝的目標路徑
    –enable-shared加此選項, 會編譯成動態鏈接庫. 不加此選項, bin目錄下的python可執行文件是靜態鏈接的
    -I(大寫的i)預處理時查找頭文件的范圍
    -L用來告訴鏈接器到哪個路徑下面去找動態鏈接庫
    -l(小寫的L)用來指定鏈接額外的庫
  • 參考
    • 源碼編譯Tkinter
    • Building Python from Source

    0.4 修改python源代碼

    0.4.1 輸出python對象的接口

  • 接口
  • [Python-2.5/Include/object.h] PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int);
  • 修改Python-2.5/Objects/intobject.c文件
  • /* ARGSUSED */ static int int_print(PyIntObject *v, FILE *fp, int flags)/* flags -- not used but required by interface */ {PyObject* str = PyString_FromString("I am in int_print"); // 用于從C中的原生字符數組中創建出python中的字符串對象/* 第二個參數指明的是輸出目標. stdout指定了輸出目標為標準輸出. 命令行激活的python, 使用的是stdout, idle的輸出目標不是stdout, 就不會顯示信息*/PyObject_Print(str, stdout, 0); printf("\n");fprintf(fp, "%ld", v->ob_ival);return 0; }
  • 設置so動態庫加載目錄
  • export LD_LIBRARY_PATH=$LOCAL/lib:$LD_LIBRARY_PATH
  • 編譯python
  • # 第一次執行configure的時候, 需要添加--enable-shared, 才能編譯成動態鏈接庫 # SVNVERSION="Unversioned directory" ./configure --prefix=<python將被安裝的目標路徑> --enable-shared make
  • 將python的位置設置到環境變量中
  • # 設置以后, 可以直接使用python在命令中啟動python交互界面 export PATH=$LOCAL/bin:$PATH
  • 在當前目錄啟動python, 就會鏈接到當前目錄的so動態庫
  • 0.4.2 重定向標準輸出

  • 重定向到文件
    • 標準輸出 sys.stdout 也是C中stdout所代表的系統標準輸出.
    • 第一次執行sys.stdout時, 輸出到屏幕
    • 第二次執行sys.stdout時, 輸出重定向到my_stdout.txt文件
  • 重定向到idle
    • 修改Python-2.5/Objects/intobject.c文件
    static PyObject * int_repr(PyIntObject *v) { /* PyInt_AsLong 將python的整數對象轉換為C中的int值.設置條件, 用來篩選出我們需要的信息*/ if(PyInt_AsLong(v) == -999) {PyObject* str = PyString_FromString("I am in int_repr");PyObject* out = PySys_GetObject("stdout");if (out != NULL) {PyObject_Print(str, stdout, 0); printf("\n");} } char buf[64];PyOS_snprintf(buf, sizeof(buf), "%ld", v->ob_ival);return PyString_FromString(buf); }
    • python交互界面
    • idle

    0.5 注意事項

    • Py_ssize_t可以看作int
    • 凡是以New結尾的API, 都以C++中的new操作符視之; 凡是以Malloc結尾的,都是以C中的malloc操作符視之。
    • 在c語言中是怎么實現繼承的?

    第一章 python對象初探(20-06-01 *)

    1.1 python內的對象

    • 在 python中, 對象就是為C中的結構體在堆上申請的一塊內存, 一般來說, 對象是不能被靜態初始化的, 并且也不能在棧空間上生存。
    • 唯一的例外就是類型對象, python中的所有的內建的類型對象(如整數類型對象, 字符串類型對象)都是被靜態初始化的。
    • 在python中,一個對象一旦被創建,它在內存中的大小就是不變的了。具有可變長度數據的對象只能在對象內維護一個指向一塊可變大小的內存區域的指針。
    • 對象的分類
    對象說明
    fundamental類型對象(type)
    numeric數值對象(integer, float, boolean)
    sequence容納其它對象的序列集合對象(string, list, tuple, set)
    mapping字典對象, 類似C++中的map的關聯對象(dict)
    internal內部對象, python虛擬機在運行時內部使用的對象(function, code, frame, module, method)

    1.1.1 對象機制的基石——PyObject

    • 在python中, 所有的東西都是對象, 而所有的對象都擁有一些相同的內容, 這些內容在PyObject中定義, PyObject是整個python對象機制的核心。
    • 查看Python-2.5/Include/object.h中的PyObject
    typedef struct _object {PyObject_HEAD } PyObject;
    • 查看Python-2.5/Include/object.h中的PyObject_HEAD
    #ifdef Py_TRACE_REFS /* Define pointers to support a doubly-linked list of all live heap objects. */ #define _PyObject_HEAD_EXTRA \struct _object *_ob_next; \struct _object *_ob_prev;#define _PyObject_EXTRA_INIT 0, 0,#else #define _PyObject_HEAD_EXTRA #define _PyObject_EXTRA_INIT #endif/* PyObject_HEAD defines the initial segment of every PyObject. */ #define PyObject_HEAD \_PyObject_HEAD_EXTRA \ // 雙向鏈表, 垃圾回收需要用到Py_ssize_t ob_refcnt; \ // 引用計數struct _typeobject *ob_type; // 指向類型對象的指針, 決定了對象的類型
    • 實際發布的python中, 是不會定義符號Py_TRACE_REFS的, PyObject的定義如下
    typedef struct _object {int ob_refcnt;struct _typeobject *ob_type; } PyObject;
    • 在python中, 對象機制的核心: 一個是引用技術,一個是類型信息.
    變量說明
    ob_refcnt與python的內存管理機制有關, 它實現了基于引用計數的垃圾收集機制。 對于某一個對象A, 當有一個新的PyObject *引用該對象時, A的引用計數應該增加;而當這個PyObject *被刪除時, A的引用計數應該減少。當A的引用計數減少到0時,A就可以從堆上被刪除, 以釋放出內存供別的對象使用
    ob_type包含對象的類型信息, 用來指定一個對象類型的類型對象
    • PyObject中定義了每一個python對象都必須有的內容, 這些內容將出現在每一個python對象所占有的內存的最開始的字節中。剩下的內存用來保存對象自己特殊的信息。
    [intobject.h] typedef struct {PyObject_HEAD // PyObjectlong ob_ival; // 保存整數值 } PyIntObject;

    1.1.2 變長對象

  • 表示變長對象的結構體
  • [object.h] #define PyObject_VAR_HEAD \PyObject_HEADint ob_size; /* Number of items in variable part 元素的個數*/typedef struct {PyObject_VAR_HEAD } PyVarObject;
    • 從PyObject_VAR_HEAD的定義可以看出,PyVarObject只是對PyObject的一個擴展而已。
    • 因此對于任何一個PyVarObject,其所占用的內存, 開始部分的字節的意義和PyObject時一樣的。
    • 在python內部, 每一個對象都擁有相同的對象頭部。這就使得在python中, 對對象的引用變得非常的統一, 我們只需要用一個PyObject *指針就可以引用任意的一個對象。 而不論該對象實際是一個什么對象。
  • python中不同對象與PyObject、PyVarObject在內存布局上的關系
  • 對象說明
    定長對象不同對象的占用的內存大小是一樣的
    變長對象不同對象占用的內存可能是不一樣的

    1.2 類型對象

    • 占用內存空間的大小是對象的一種元信息, 這樣的元信息是與對象所屬類型密切相關的, 因此它一定會出現在與對象所對應的類型對象中. 現在我們可以來詳細考察一下類型對象_typeobject:
    [object.h] typedef struct _typeobject {PyObject_VAR_HEADchar *tp_name; /* For printing, in format "<module>.<name>" */int tp_basicsize, tp_itemsize; /* For allocation *//* Methods to implement standard operations */destructor tp_dealloc;printfunc tp_print;....../* More standard operations (here for binary compatibility) */hashfunc tp_hash;ternaryfunc tp_call;......} PyTypeObject;
    • 類型名, tp_name, 主要是python內部以及調試的時候使用.
    • 創建該類型對象時分配內存空間大小的信息, 即tp_basicsize和tp_itemsize.
    • 與該類型對象相關連的操作信息(就是諸如tp_print這樣的許多的函數指針).
    • 類型的類型信息.
    • PyTypeObject 對應python中的類

    1.2.1 對象的創建

  • Python C API 創建
    • 范型的API(或者稱為AOL(Abstract Object Layer). 這類API都具有諸如PyObject_***的形式, 可以應用在任何Python對象身上
    // API內部會有一整套機制確定最終調用的函數是哪一個 PyObject_Print(int object); PyObject_Print(string object); PyObject* intObj = PyObject_New(PyObject, &PyInt_Type); // 創造一個整數對象
    • 類型相關的API(或者稱為COL(Concrete Object Layer). 這類API通常只能作用在某一種類型的對象上, 對于每一種內建對象, Python都提供了這樣的一組API.
    // 創建一個值為10的整數對象 PyObject *intObj = PyInt_FromLong(10);
  • 無論采用哪種C API, python內部最終都是直接分配內存, 因為python對于內建對象是無所不知的. 對于自定義的類A, python不可能事先提供PyA_New這樣的API. 對于這種情況, python會通過A所對應的類型對象創建實例對象.
    • 實際上, 在python完成運行環境的初始化之后, 符號"int"就對應著一個表示為<type ‘int’>的對象, 這個對象其實就是python內部的PyInt_Type.
    ''' 在new style class中, int是一個繼承自object的類型, 類似于int對應著python內部的PyInt_Type, object在python內部則對應著PyBaseObject_Type ''' int(10) # 實際上是通過PyInt_Type創建了一個整數對象
    • 使用C++中定義int這種類型的方式, 以及在python內部, 這種繼承關系是如何實現的。
      說明:標上序號的虛線箭頭代表了創建整數對像的函數調用流程, 首先PyInt_Type(對應python中的int類)中的tp_new會被調用, 如果這個tp_new為NULL(真正的PyInt_Type中并不為NULL, 這里只是舉例說明這種情況), 那么會到tp_base指定的基類中去尋找tp_new操作, PyBaseObject_Type(對應python中的object基類)的tp_new指向了object_new. 新式類中,所有的類都是以object為基類的, 所以最終會找到一個不為NULL的tp_new. 在object_new中, 會訪問PyInt_Type中記錄的tp_basicsize信息, 繼而完成申請內存的操作。這個信息記錄著一個整數對象應該占用多大內存, 在python源碼中, 這個值被設置成了sizeof(PyIntObject). 在調用tp_new完成“創建對象”之后, 流程會轉向PyInt_Type的tp_init, 完成“初始化對象”的工作。對應到C++中, tp_new可以視為new操作符, 而tp_init則可視為類的構造函數。(這里只說明了類型對象在實例對象創建過程中的作用,實際上, 會有些不同)

    1.2.2 對象的行為

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

    • 比如PyTypeObject中的tp_hash指明對于該類型的對象, 如何生成其hash值。我們看到tp_hash是一個hashfunc類型的變量, 在object.h中, hashfunc實際上是一個函數指針: typedef long (*hashfunc)(PyObject *)
    • 在這些操作信息中, 有三組非常重要的操作族, 在PyTypeObject中, 它們是tp_as_number、tp_as_sequence、tp_as_mapping. 他們分別指向PyNumberMethods、PySequenceMethods 和PyMappingMethods函數族。PyNumberMethods函數族示例:
    [object.h] typedef PyObject * (*binaryfunc)(PyObject *, PyObject *);typedef struct {binaryfunc nb_add; // 指定了數值對象進行加法操作時的具體行為binaryfunc nb_subtract;... } PyNumberMethods;
    • 對于一種類型來說, 它完全可以同時定義三個函數族中的所有操作。 下面示例中, 既有數值對象的特性, 也有關聯對象的特性:

    1.2.3 類型的類型

    • 通過PyType_Type來確定一個對象為類型對象
    [typeobject.c] /* 所有用戶自定義class所對應的PyTypeObject對象都是通過PyType_Type這個對象創建的 PyTypeObject PyType_Type = {PyObject_HEAD_INIT(&PyType_Type)0, /* ob_size */"type", /* tp_name */sizeof(PyHeapTypeObject), /* tp_basicsize */sizeof(PyMemberDef), /* tp_itemsize */... };
    • PyType_Type 與一般PyTypeObject的關系
    • PyInt_Type和PyType_Type是怎么建立關系的。在python中, 每一個對象都將自己的引用計數、類型信息保存在開始的部分中。為了方便對這部分內存的初始化, python中提供了幾個有用的宏:
    [object.h] #ifdef Py_TRACE_REFS /* Define pointers to support a doubly-linked list of all live heap objects. */ #define _PyObject_HEAD_EXTRA \struct _object *_ob_next; \struct _object *_ob_prev;#define _PyObject_EXTRA_INIT 0, 0,#else #define _PyObject_HEAD_EXTRA #define _PyObject_EXTRA_INIT #endif /* PyObject_HEAD defines the initial segment of every PyObject. */ #define PyObject_HEAD \_PyObject_HEAD_EXTRA \Py_ssize_t ob_refcnt; \struct _typeobject *ob_type;#define PyObject_HEAD_INIT(type) \_PyObject_EXTRA_INIT \1, type,
    • PyObject和PyVarObject的定義
    [object.h] /* PyObject_VAR_HEAD defines the initial segment of all variable-size* container objects. These end with a declaration of an array with 1* element, but enough space is malloc'ed so that the array actually* has room for ob_size elements. Note that ob_size is an element count,* not necessarily a byte count.*/ #define PyObject_VAR_HEAD \PyObject_HEAD \Py_ssize_t ob_size; /* Number of items in variable part */ #define Py_INVALID_SIZE (Py_ssize_t)-1/* Nothing is actually declared to be a PyObject, but every pointer to* a Python object can be cast to a PyObject*. This is inheritance built* by hand. Similarly every pointer to a variable-size Python object can,* in addition, be cast to PyVarObject*.*/ typedef struct _object {PyObject_HEAD } PyObject; typedef struct { PyObject_VAR_HEAD } PyVarObject;
    • 以PyInt_Type為例, 可以更清晰地看到一般的類型對象和這個特立獨行的PyType_Type對象之間的關系
    [intobject.c] PyTypeObject PyInt_Type = {PyObject_HEAD_INIT(&PyType_Type) // *_ob_next 0, *_ob_prev 0, ob_refcent 1, ob_type &PyType_Type0, // ob_size 0"int",sizeof(PyIntObject),... };
    • PyIntObject
    typedef struct {PyObject_HEADlong ob_ival; } PyIntObject;
    • 整數對象運行時的圖像表現
    變量說明
    ob_type指向對象的類型
    tp_base指向對象的父類

    1.3 Python對象的多態性

    通過PyObject和PyTypeObject, python利用C語言完成了C++所提供的對象的多態的特性. 在python創建一個對象, 比如PyIntObject對象時, 會分配內存, 進行初始化. 然后python內部會用一個PyObject *變量來保存和維護這個對象. 其它對象也與此類似, 所以在python內部各個函數之間傳遞的都是一種泛型指針-PyObject *. 這個指針所指的對象究竟是什么類型的, 只能從指針所指對象的ob_type域動態進行判斷, 而正是通過這個域, python實現了多態機制.

    • Print函數示例
      如果傳給Print 的指針是一個PyIntObject*, 那么它就會調用PyIntObject對象對應的類型對象中定義的輸出操作, 如果指針是一個PyStringObject*, 那么就會調用PyStringObject對象對應的類型對象中定義的輸出操作.
    void Print(PyObject* object) {object->ob_type->tp_print(object); }
    • Hash函數示例
    [object.c] long PyObject_Hash(PyObject *v) {PyTypeObject *tp = v->ob_type;if (tp->tp-hash != NULL)return (*tp->tp_hash)(v); // tp前面的*, 有和沒有都是一樣的. -> 的優先級大于*... }

    1.4 引用計數

  • c和c++, 可以由程序員申請和釋放內存. 容易造成內存泄露
  • python由語言本身負責內存的管理和維護, 即采用垃圾收集機制. 提高開發效率, 降低bug發生幾率. 損失了一部分運行效率. python通過對一個對象的引用技術的管理來維護對象在內存中的存在與否. python中的每一個東西都是對象, 都有一個ob_refcnt變量. 這個變量維護著該對象的引用計數, 從而也最終決定著該對象的創建與消亡.
  • 在python中, 主要通過Py_INCREF(op)和Py_DECREF(op)兩個宏來增加和減少一個對象的引用計數. 當一個對象的引用技術減少到0之后, Py_DECREF將調用該對象的析構函數(借用c++的術語, 實際是對象對應的類型對象中定義的函數指針tp_dealloc來指定的)來釋放該對象所占有的內存和系統資源. 但是調用析構函數并不意味著最終一定會調用free釋放內存空間, 如果真是這樣的話, 那頻繁地申請, 釋放內存空間會使python的執行效率大打折扣. 一般來說, python中大量采用了內存對象池的技術, 使用這種技術可以避免頻繁地申請和釋放內存空間. 因此在析構時, 通常都是將對象占用的空間歸還到內存池中.
  • 從python的對象體系來看, 各個對象提供了不同的對象銷毀事件處理函數, 而事件的注冊動作正是在各個對象對應的類型對象中靜態完成的.
  • PyObject中的ob_refcnt是一個32位的整形變量.
  • 類型對象, 永遠不會被析構. 每一個對象中指向類型對象的指針不被視為對類型對象的引用.
  • 在每一個對象創建的時候, python提供了一個_Py_NewReference(op)宏來將對象的引用計數初始化為1.
  • python在最終發行時, 引用計數的宏所對應的實際代碼.
  • [object.h] #define _Py_NewReference(op) ((op)->ob_refcnt = 1) #define _Py_dealloc(op) ((*(op)->ob_type->tp_dealloc)((PyObject *)(op))) #define Py_INCREF(op) ((op)->ob_refcnt++) #define Py_DECREF(op)if (--(op)->ob_refcnt != 0);else_Py_Dealloc((PyObject *)(op)) /* Macros to use in case the object pointer may be NULL; */ #define Py_XINCREF(op) if ((op) == NULL); else Py_INCREF(op) #define Py_XDECREF(op) if ((op) == NULL); else PY_DECREF(op)

    第2章 python中的整數對象

    2.1 初識PyIntObject對象

    • python中的 “整數” 這個概念的實現是通過PyIntObject對象來完成的.
    • 對象分類
      根據對象維護數據的可變性將對象分為: 可變對象、不可變對象
      根據同一類型不同對象占用的內存大小是否相同可將對象分為: 定長對象、變長對象
    • PyIntObject的元信息實際上保存在對應的類型對象PyInt_Type中
    [intobject.c] PyTypeObject PyInt_Type = {PyObject_HEAD_INIT(&PyType_Type) // 初始化頭部0, // 因為PyTypeObject是一個PyVarObject對象, 因此這里需要設置大小為0"int", // 用來打印的字段,比如我們type(3)返回的int字符串就是來自這里。sizeof(PyIntObject), // 對象基本大小0, // 如果是可變大小對象,這個字段是對象里面存儲項的大小(destructor)int_dealloc, /* tp_dealloc */(printfunc)int_print, /* tp_print */0, /* tp_getattr */0, /* tp_setattr */(cmpfunc)int_compare, /* tp_compare */(reprfunc)int_repr, /* tp_repr */&int_as_number, /* tp_as_number 確定了對于一個整數對象, 這些數值操作應該如何進行*/ 0, /* tp_as_sequence */0, /* tp_as_mapping */(hashfunc)int_hash, /* tp_hash */0, /* tp_call */(reprfunc)int_repr, /* tp_str */PyObject_GenericGetAttr, /* tp_getattro */0, /* tp_setattro */0, /* tp_as_buffer */Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |Py_TPFLAGS_BASETYPE, /* tp_flags */int_doc, /* tp_doc */0, /* tp_traverse */0, /* tp_clear */0, /* tp_richcompare */0, /* tp_weaklistoffset */0, /* tp_iter */0, /* tp_iternext */int_methods, /* tp_methods */0, /* tp_members */0, /* tp_getset */0, /* tp_base */0, /* tp_dict */0, /* tp_descr_get */0, /* tp_descr_set */0, /* tp_dictoffset */0, /* tp_init */0, /* tp_alloc */int_new, /* tp_new */(freefunc)int_free, /* tp_free */ }; 方法說明
    int_deallocPyIntObject對象的析構操作
    int_freePyIntObject對象的釋放操作
    int_repr轉化成PyStringObject對象
    int_hash獲得HASH值
    int_print打印PyIntObject對象
    int_compare比較操作
    int_as_number數值操作集合
    int_methods成員函數集合

    2.2 PyIntObject對象的創建和維護

    2.2.1 對象創建的3中途徑

    • 在python自身的實現中, 幾乎都是調用C API來創建內建實例對象的。
    • 示例
    [intobject.h] /* * 其中PyInt_FromUnicode最終調用PyInt_FromString, 而PyInt_String最終調用PyInt_FromLong */ PyObject *PyInt_FromLong(long ival) PyObject *PyInt_FromString(char *s, char **pend, int base)#ifdef Py_USING_UNICODEPyObject *PyInt_FromUnicode(Py_UNICODE *s, int length, int base) #endif

    2.2.2 小整數對象

    • 為了性能考慮,python中對小整數有專門的緩存池,這樣就不需要每次使用小整數對象時去用malloc分配內存以及free釋放內存。
    • 小整數的范圍默認設定為[-5, 257)
    #ifndef NSMALLPOSINTS // Number_Small_Positive_Integers #define NSMALLPOSINTS 257 #endif #ifndef NSMALLNEGINTS // Number_Small_Negtive_Integers #define NSMALLNEGINTS 5 #endif #if NSMALLNEGINTS + NSMALLPOSINTS > 0 /* References to small integers are saved in this array so that theycan be shared.The integers that are saved are those in the range-NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive). */ static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS]; // 小整數對象的對象池, 為PyIntObject對象指針數組。[]符號的優先級大于* #endif

    2.2.3 大整數對象

    • python運行環境將提供一塊內存空間, 這些內存空間由這些大整數輪流使用。
    [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; // 這個結構里維護了一塊內存(block), 其中保存了一些PyIntObject對象static PyIntBlock *block_list = NULL; static PyIntObject *free_list = NULL;

    2.2.4 添加和刪除

    • 詳解PyInt_FromLong
    [intobject.c] PyObject* PyInt_FromLong(long ival) {register PyIntObject *v; #if NSMALLNEGINTS + NSMALLPOSINTS > 0if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {v = small_ints[ival + NSMALLNEGINTS];Py_INCREF(v); #ifdef COUNT_ALLOCSif (ival >= 0)quick_int_allocs++;elsequick_neg_int_allocs++; #endifreturn (PyObject *) v;} #endifif (free_list == NULL) {if ((free_list = fill_free_list()) == NULL)return NULL;}/* Inline PyObject_New */v = free_list;free_list = (PyIntObject *)v->ob_type;PyObject_INIT(v, &PyInt_Type);v->ob_ival = ival;return (PyObject *) v; }

    2.2.4.1 使用小整數對象池

    如果NSMALLNEGINTS + NSMALLPOSINTS > 0成立, 那么python認為小整數對象池機制被激活了, 此時會創建對象池(PyIntObject指針數組). PyInt_FromLong會首先檢查傳入的long值是否屬于小整數的范圍, 如果確實屬于小整數, 一切就變得簡單了, 只需要返回在小整數對象池中的對應的對象就可以了。
    如果小整數對象池機制沒有被激活, 或者傳入的long值不是屬于小整數, python就會轉向由block_list維護的通用整數對象池。 正如前面我們所描述的, python需要在通過free_list在某塊block的objects中, 尋找一塊可用于存儲新的PyIntObject對象的內存。

    2.2.4.2 創建通用整數對象池

    顯然,當首次調用PyInt_FromLong時,free_list必定為NULL, 這時python會調用fill_free_list, 創建新的block, 從而也就創建了新的空閑內存。需要注意的是, python對fill_free_list的調用不光會發生在對PyInt_FromLong的首次調用時,在python運行期間, 只要所有的block的空閑內存都被使用完了, 就會導致free_list變為NULL, 從而在下一次PyInt_FromLong的調用時激發對fill_free_list的調用。

    [intobject.c] static PyIntObject * fill_free_list(void) {PyIntObject *p, *q;/* Python's object allocator isn't appropriate for large blocks. */// 申請大小為sizeof(PyIntBlock)的內存空間, 并鏈接到已有的block list中p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));if (p == NULL)return (PyIntObject *) PyErr_NoMemory();((PyIntBlock *)p)->next = block_list;block_list = (PyIntBlock *)p;/* Link the int objects together, from rear to front, then returnthe address of the last int object in the block. */// 將PyIntBlock中的PyIntObject數組——objects-轉變成單向鏈表p = &((PyIntBlock *)p)->objects[0];q = p + N_INTOBJECTS;while (--q > p)q->ob_type = (struct _typeobject *)(q-1);q->ob_type = NULL;return p + N_INTOBJECTS - 1; }

    在fill_free_list中, 會首先申請一個新的PyIntBlock結構,如圖所示

    這時block中的objects還僅僅是一個PyIntObject對象的數組, 接下來, python將objects中的所有PyIntObject對象通過指針依次連接起來, 從而將數組轉變成一個單向鏈表。 python從objects數組的最后一個元素開始鏈接的過程, 在整個鏈接過程中, python使用了PyObject中的ob_type指針作為 連接指針。

    2.2.4.3 使用通用整數對象池

    FQA

    • PyIntObject是怎么知道它的元信息為PyInt_Type的?
      對象是由類創建的, 在創建對象的過程, 就會賦給類型對象相應的元信息
    • PyObject中的雙向鏈表是怎么用來進行垃圾回收的?
    • PyType_Type和PyTypeObject?
      所有用戶自定義class所對應的PyTypeObject對象都是通過PyType_Type這個對象創建的
    • 靜態的整數對象的定義
    [intobject.h] // python中的整數對象PyIntObject實際上就是對C中原生類型long的一個簡單包裝 typedef struct {PyObject_HEADlong ob_ival; } PyIntObject;
    • c語言中是怎么判斷加法是否溢出的?

    總結

    以上是生活随笔為你收集整理的python源码剖析笔记的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。