Python源码剖析-深度探索动态语言核心技术
Python源碼剖析-深度探索動態語言核心技術
- 內容簡介
- Python 總體架構
- Python源代碼的組織
- 第一部分:Python內建對象
- 第1章:Python對象初探
- 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 引用計數
- 1.5 Python 對象的分類
- 第2章:Python中的整數對象
- 2.1 初識 PyIntObject 對象
- 2.2 PyIntObject 對象的創建和維護
- 2.2.1 對象創建的3種途徑
內容簡介
為了更好地利用python語言,無論是python本身還是和C/C++配合使用,深刻理解python的運行原理都是非常重要的,本文以CPython源碼為研究對象,深入剖析Python的實現。主要包含:
- Python內建對象
- Python虛擬機
- Python高級特性
本文參考: Python源碼剖析一書
學習建議:唯有親身嘗試,才能深解其中三昧。 在真實的源碼和文章的描述相比較揣摩,動手搗鼓搗鼓Python的源碼,用真實的輸出來驗證文章描述和自己的理解。
Python 總體架構
首先我們應該了解python的整體架構,對python 有個宏觀的認識。
Python整體架構分為三部分,如下圖:
- 左邊是python提供的大量模塊,庫,以及用戶自定義模塊
- 右邊是python的運行時環境,包括對象/類型系統, 內存分配器, 運行時狀態信息。
- 中間部分為解釋器(虛擬機), 包括詞法分析,語法分析,編譯器與 (虛擬機Code Evauator)
Python源代碼的組織
獲取源碼網站: 源碼網站
解壓后我們將看到目錄結構:
- Includes : 該目錄包含了python提供的所有頭文件,我們如果用c、c++編寫自定義模塊擴展python,就需要用到這里提供的頭文件
- Lib:包含了python自帶的所有標準庫, lib 中的庫都是用python編寫而成
- Modules: 該模塊包含了所有用C語言編寫的模塊, 比如random,cstringIO等,其中模塊都是那些對速度要求非常嚴格的模塊, (對速度沒有嚴格要求的放在了lib中)
- Parser: 包含了python解釋器中的詞法分析Scanner和語法分析 Parser部分
- Objects: 改目錄中包含了所有Python的內建對象,包括整數,list, dict等, 同時包含了Python在運行時需要的所有的內部使用對象的實現。
- Python:是Python解釋器的 Compiler 和執行引擎部分,是其運行的核心所在。
第一部分:Python內建對象
第1章:Python對象初探
我們熟知在面向對象理論中的 “類” 和 “對象” 這兩個概念在python中都是使用Python內對象來實現的,也可以理解為一切都是對象, 于是我們將會帶著一些疑問進入本章~
額外一些有意思的相關知識:
- 在Python中,對象是C中結構體在堆上申請的一塊內存,因此對象不能被靜態初始化,也不能在棧上生存,例外是(Python的類型對象type,都是被靜態初始化的 ~)
- 在Python中,一個對象一旦被創建,其內存大小就是不變的了,于是python在對象內維護一個指向一塊可變大小的內存區域的指針來容納可變長度數據的對象,當然這也是因為遵循這樣的規則是的通過指針維護對象的工作變得簡單。(struct _typeobject *ob_type)
1.1 Python內的對象
1.1.1 對象的基石-----PyObject
所有的對象都擁有一些相同的內容, 這些內容就在PyObject中定義, PyObject是整個Python對象體系的核心。
而PyObject的秘密就在這個PyObject_HEAD中~
我們發現,其實在PyObject的定義中, 僅有兩個東西:
- int (py_ssize_t) 類型的 ob_refcnt , 對象引用計數(垃圾回收機制)
- struct _typeobject *ob_type, 一個指針,來維護一個類型信息結構(后續會再次分析)
PyObject中定義了每一個Python對象都必須有的內容, 每一個Python對象除了擁有PyObject外,還會占據一些額外的內存,保存屬于自己的特殊信息,比如 intobject就會多出一個 ob_ival 來表示其值。
1.1.2 定長對象
對于數組,這種 n 個 元素構成的東西也是一類Python對象的共同特征, 因此, 在PyObject對象之外
還有一個表示這類對象的結構體–PyVarObject
變長對象往往都是容器, ob_size 指明了所容納元素的個數
重要點總結
從PyObject_VAR_HEAD的定義中我們發現, PyVarObject實際上只是對PyObject的擴展而已,其開始部分的字節的意義和 PyObject相同, 一個引用計數加上一個類型信息結構指針,這意味著在Python中對對象的引用變得十分的統一, 使用 PyObject* 指針能引用任意的一個對象。
不同的Python對象在內存布局上的關系1.2 類型對象
在上文我們看到了Python中所有對象的共有信息的定義,但我們會思考,我們所創建的對象的真實描述信息在哪呢? 例如:Int 和 Str 對象占有的空間信息在哪~, 其實這些信息都被隱含在 PyObject 的 _typeobjct 指針指向的地方。
在_typeobjct 的定義中包含了很多的信息,主要分為4類:
- 類型名
- 創建該類型時分配的內存空間大小信息 (tp_basicsize 和 tp_itemsize)
- 與該類型對象相關聯的操作信息(諸如 tp_print這樣的許多的函數指針)
- 類型的類型信息
事實上, 一個 PyTypeObject對象就是 Python中對面向對象理論中“類”這個概念的實現,由于PyTypeObject是一個篇幅較大的話題,我們在第二部分來介紹構建在PyTypeObject之上的Python的類型和對象體系。
1.2.1 對象的創建
思考: Python內部究竟如何才能從無到有創建一個整數對象呢?
Python對外提供了C API,分為兩種:
- AOL,(Abstract Object Layer),形式如 PyObject_***形式, 可以應用在任何Python對象上
- COL (Concrete Object Layer) 只能作用于某一種類型的對象上,對于內建對象都有一套API
對于自定義的類型,比如 Class A(object) 定義的類型A,要創建其對象,由于Python不可能事先提供
PyA_New這樣的API, 他將會通過A所對應的類型對象 來創建實例對象。
下面我們舉例創建整數對象的函數調用流程(如圖):
- PyInt_Type 中的 tp_new 會被調用,如果tp_new 是空,會到基類找tp_new
- tp_new會訪問PyInt_Type中記錄的 tp_basicsize 信息,完成申請內存的操作
- 之后調用 tp_init ,完成初始化的操作
tp_new, tp_init 對應 new操作符和類的構造函數
1.2.2 對象的行為
對象的行為是通過內置大量函數指針來實現的,這些函數指針就表現了類型對象所定義的操作(行為)。
同時,在PyTypeObject有三個重要的操作族需要介紹一下:
tp_as_number, tp_as_sequence, tp_as_mapping,
分別指向 PyNumberMethods, PySequenceMethods 和 PyMappingMethods 函數族。
這里我們以序列操作族為例子:
也可以說當 tp_as_sequence 指針不為空時(list,str),表現出的行為就是序列行為。
1.2.3 類型的類型
在我們的PyTypeObject定義的最開始也會存在 PyObject_Var_HEAD, 證明Python中的類型本身也是一個對象,其類型是 PyType_Type:
- PyType_Type 在 Python的類型機制是非常關鍵的,所有用戶自定義Class所對應的 PyTypeObject對象都是通過這個對象創建的。
這里我們來看一下Int類型的定義,它會調用 PyObject_HEAD_INIT這個宏來初始化公共的頭部分:
本質上就是將 ob_type 指向 PyType_Type, 并將其引用計數設置為1
現在我們可以想象一個整數對象在運行時的形象表示:
1.3 Python對象的多態性
Python利用C語言實現了對象的多態性,Python內部在創建對象時會使用 PyObject * 取保存和維護這個對象,(所有對象的頭部是相同的),因此直接可以使用該指針所指對象的 ob_type 域動態去判斷,正是這個域的存在,Python實現了多態性。
我們來分析一下:
void Print(PyObject* object) {object->ob_type.tp_print(object); }如果指針本身是一個PyIntObject* ,就會調用到 PyIntObject的類型對象中定義的輸出操作,
如果是一個PyStringObject*, 就會調用到 PyStringObject 對象對應的類型對象中定義的輸出操作。
1.4 引用計數
Python內建了垃圾回收機制,進行較為繁重的內存管理工作,引用計數正是Python垃圾回收機制的一部分。
- Python中每一個東西都有一個 ob_refcnt 變量,維護著引用計數,決定著對象的創建和消亡。
- 通過 Py_INCREF(op) 和 Py_DECREF(op) 兩個宏來增加和減少一個對象的引用計數。
- 當引用計數為0,會調用該對象的 tp_dealloc 進行析構動作
注意: 析構函數并不意味著最終會釋放內存, 頻繁申請和釋放會有效率上的問題,因此Python中大量采用了內存池的技術。
1.5 Python 對象的分類
我們將 Python 對象從概念上大致分為5類:
- Fundamental 對象: 類型對象
- Numeric 對象: 數值對象
- Sequence對象:容納其他對象的序列集合對象
- Mapping對象:關聯對象
- Internal對象: Python 虛擬機在運行時內部使用的對象
第2章:Python中的整數對象
2.1 初識 PyIntObject 對象
在定義上,是對 c 語言 long 的擴展而已~
對于其 ob_type 的指向,則是指向了 PyInt_Type
在PyInt_Type 中保存了關于PyIntObject對象的豐富信息,不僅有其對象應占有的內存大小,文檔信息,也包括了支持的操作。
操作定義: 需要注意的是 int_as_number 這個域,他是一個PyNumberMethods對象,PyNumberMethods 有39個函數指針,定義了39種可選的操作(加減乘除),而對于 int_as_number 確定了對于一個整數對象,數值操作是如何進行的。
小知識點: 在整數的相加 int_add 中檢查了加法結果是否溢出,用了位運算。
原理:只有兩個相同符號的整數相加時才會溢出, 一正一負相加不會溢出, 溢出后得到的結果符號一定與任意一個整數符號相反,即
2.2 PyIntObject 對象的創建和維護
2.2.1 對象創建的3種途徑
內建類型對象的tp_new, tp_init 操作來創建實例對象, 最終依舊是會調用Python為特定內建對象準備的
CAPI。 為了創建一個 PyIntObject對象,Python提供了3條途徑。
總結
以上是生活随笔為你收集整理的Python源码剖析-深度探索动态语言核心技术的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: POJ 3090 Visible Lat
- 下一篇: centos7 firewall防火墙实