標籤:log 通過 就是 ash blog 學習 完成 支援 dea
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建立對象的方法:
- 通過Python C API來建立
- 通過類型對象 PyInt_Type來建立
Python對外提供了C API讓使用者可以從C環境中與Python互動,Python的C API分為兩類:
- 範型API,或稱為AOL
- 另一類稱為COL
從PyInt_Type建立整數對象
說明: 在Python完成運行環境的初始化後,符號‘int‘就對應著Python內部的PyInt_Type對象,object則對應著PyBaseObject_Type對象
- 使用init(10)建立對象時,就會調用PyInt_Type對象中的tp_new操作
- 假如這一個tp_new操作不存在,為NULL,那麼就到tp_base指定的基類裡去找tp_new操作
- 在Python2.2以後的新式類中,所有的類都是以object為基類的,所以最終一定可以找到一個不為NULL的tp_new
- tp_new指向了object_new,object_new會訪問PyInt_Type中記錄的tp_basicsize資訊,得到需要申請的記憶體大小,然後完成申請記憶體的操作。我們前面說了,“在Python中,對象就是為C中的結構體在堆上申請的一塊記憶體”,所以記憶體申請完成就代表對象已經建立(但還未初始化)
- 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部分是#define指令本身
- 第2部分是選定的縮寫,也稱為宏,有些宏代表值,這些宏被稱為類對象宏
- 第3部分稱為其他清單或替換體
從宏變成最終替換文本的過程稱為宏展開
在#define中使用參數可以建立外形和作用與函數類似的類函數宏
《Python源碼剖析》