色屁屁www影院免费观看入口,欧美性猛交ⅹxxx乱大交妖精,欧美激情第5页,欧美性猛交xxxx三人,欧美一a级做爰片大开眼界

達(dá)觀動(dòng)態(tài)

達(dá)觀愿與業(yè)內(nèi)同行分享 助力各企業(yè)在大數(shù)據(jù)浪潮來(lái)臨之際一起破浪前行

Python源碼剖析:深度探索Cpython對(duì)象-達(dá)觀數(shù)據(jù)

 

CPython 是 Python 社區(qū)的標(biāo)準(zhǔn),其他版本的 Python,比如 pypy,都會(huì)遵行 CPython 的標(biāo)準(zhǔn) API 實(shí)現(xiàn)。想要更深入的認(rèn)識(shí) Python,就需要了解 CPython 的源碼實(shí)現(xiàn)。本文將從 CPython 的對(duì)象構(gòu)造器開(kāi)始入手,帶大家揭開(kāi) CPython 源碼的面紗,帶你進(jìn)入 C + Python 的世界。文章的最后,你也會(huì)對(duì) Python 中最重要的概念:一切皆對(duì)象 (Object) 有更深刻的認(rèn)識(shí);你還會(huì)發(fā)現(xiàn)一些具體的線索,為什么 Python 用起來(lái)比其他靜態(tài)類型語(yǔ)言慢很多。

一、為什么要學(xué)習(xí) Python 源碼

Python 是一門(mén)上層語(yǔ)言,創(chuàng)建者通過(guò)有意設(shè)計(jì)來(lái)隱藏背后復(fù)雜的細(xì)節(jié) (builtins)。在解決項(xiàng)目問(wèn)題時(shí),很多問(wèn)題也許能通過(guò)搜索引擎找到答案,但 Python 是一門(mén)迭代速度非常快的語(yǔ)言,搜索引擎與專業(yè)書(shū)難以獲得實(shí)效性好且準(zhǔn)確的答案,因此多了解其架構(gòu)與核心原理,可以更好地理解Python語(yǔ)言的使用方式、提高編程技能和調(diào)試能力。

二、CPython 整體架構(gòu)

CPython 整體架構(gòu)大致分為三個(gè)模塊:

  1. 代碼文件 File Groups?– Python 所提供的的大量的模塊、庫(kù)、以及用戶自定義的模塊。用戶還可以通過(guò)自定義模塊來(lái)擴(kuò)展 Python 系統(tǒng)。
  2. 解釋器 Python Core?– 又稱 Python 虛擬機(jī),對(duì)代碼分析理解,翻譯成字節(jié)流,并運(yùn)行這些字節(jié)代碼。·?Scanner 負(fù)責(zé)詞法分析的工作,將代碼一行一行切分為 Token·?Parser 則負(fù)責(zé)語(yǔ)法分析,將 Token 組織為抽象語(yǔ)法樹(shù)

    ·?Compiler 則將語(yǔ)法樹(shù)轉(zhuǎn)化為指令集合的字節(jié)碼流

    ·?Code Evaluator 也是我們常說(shuō) Python 虛擬機(jī),負(fù)責(zé)執(zhí)行這些字節(jié)碼

  3. 運(yùn)行環(huán)境 Runtime Env?– 包括運(yùn)行時(shí)的對(duì)象、基礎(chǔ)類型結(jié)構(gòu)、內(nèi)存分配器和實(shí)時(shí)的運(yùn)行狀態(tài)信息。·?Object 和 Type Structure 分別是程序在運(yùn)行過(guò)程中生成的對(duì)象和Python中的自帶內(nèi)建對(duì)象,如 Int、Str、List 等·?Memory Allocator 則負(fù)責(zé)申請(qǐng)創(chuàng)建對(duì)象需要的內(nèi)存,本質(zhì)就是封裝了 C 語(yǔ)言里面的 malloc() 函數(shù)

    ·?Current State 負(fù)責(zé)維護(hù)運(yùn)行時(shí)的各類狀態(tài)信息,以便在程序執(zhí)行過(guò)程中如果發(fā)生狀態(tài)變化(正常態(tài)和異常態(tài))時(shí),仍然能正常運(yùn)行

三、編譯 CPython

我們可以從下文的 GitHub 地址下載各版本的 CPython 源代碼(本文內(nèi)容以 Python 3.11 為例),其目錄結(jié)構(gòu)如下:

接下來(lái),我們將從源代碼編譯 CPython。此步驟需要 C 編譯器和一些構(gòu)建工具。不同的系統(tǒng)編譯方法也不同,這里我用的是 mac 系統(tǒng)。

在上述命令中,你需要下載并安裝一些工具,包括 Homebrew,Git,Make, GNU C 編譯器和OpenSSL等。./configure步驟用來(lái)自動(dòng)化構(gòu)建過(guò)程,CPPFLAGS 是 c 和 c++ 編譯器的選項(xiàng),這里指定了 zlib 頭文件的位置,LDFLAGS 是 gcc 等編譯器會(huì)用到的一些優(yōu)化參數(shù),這里是指定了 zlib 庫(kù)文件的位置,(brew –prefix openssl) 顯示的是 openssl 的安裝路徑,運(yùn)行完上面命令以后在存儲(chǔ)庫(kù)的根目錄中會(huì)生成一個(gè) Makefile,你可以通過(guò)運(yùn)行以下命令來(lái)構(gòu)建 CPython 二進(jìn)制文件。make -j2-j2 標(biāo)志允許 make 同時(shí)運(yùn)行 2 個(gè)作業(yè)來(lái)加快編譯速度。在構(gòu)建期間,你可能會(huì)收到一些錯(cuò)誤,例如,dbm,sqlite3,uuid,nis,ossaudiodev,spwd 和tkinter 將無(wú)法使用這組指令構(gòu)建。如果你不打算針對(duì)這些軟件包進(jìn)行開(kāi)發(fā),這些錯(cuò)誤沒(méi)什么影響。構(gòu)建將花費(fèi)幾分鐘并生成一個(gè)名為 python.exe 的二進(jìn)制文件,雖然它的后綴是 exe 格式,但它確實(shí)是 macOS 下的可執(zhí)行文件。每次改動(dòng)源代碼,都需要重新運(yùn)行 make 進(jìn)行編譯。

四、了解 Python 對(duì)象

(一)PyObject 和 PyVarObject

Python 中一切皆對(duì)象,而所有的對(duì)象都擁有一些共同的信息(也叫頭部信息),這些信息就在 PyObject 中,PyObject 是 Python 整個(gè)對(duì)象機(jī)制的核心,是 CPython 對(duì)象構(gòu)造器的基石,我們來(lái)看看它的定義:

因此我們看到 PyObject 的定義非常簡(jiǎn)單,就是一個(gè)引用計(jì)數(shù)和一個(gè)類型指針,所以 Python 中的任意對(duì)象都必有引用計(jì)數(shù)和類型這兩個(gè)屬性。針對(duì)變長(zhǎng)對(duì)象,Python 底層也提供了一個(gè)結(jié)構(gòu)體,因?yàn)?Python 里面很多都是變長(zhǎng)對(duì)象。我們來(lái)看看 PyVarObject 的定義:

例如列表(PyListObject 實(shí)例)中的 ob_size 維護(hù)的就是列表的元素個(gè)數(shù),插入一個(gè)元素,ob_size 會(huì)加1,刪除一個(gè)元素,ob_size 會(huì)減1。因此,我們使用 len 獲取列表的元素個(gè)數(shù)是一個(gè)時(shí)間復(fù)雜度為 O(1) 的操作,因?yàn)?ob_size 始終和內(nèi)部的元素個(gè)數(shù)保持一致,使用 len 獲取元素個(gè)數(shù)的時(shí)候會(huì)直接訪問(wèn) ob_size。

(二)PyTypeObject 類型對(duì)象

而將一個(gè)對(duì)象和其類型對(duì)象關(guān)聯(lián)起來(lái)的,毫無(wú)疑問(wèn)正是該對(duì)象內(nèi)部的 PyObject 中的 ob_type,也就是類型指針。我們通過(guò)對(duì)象的 ob_type 成員即可獲取類型對(duì)象的指針,通過(guò)該指針可以獲取存儲(chǔ)在類型對(duì)象中的某些元信息。我們來(lái)看看 _typeobject 的幾個(gè)關(guān)鍵的成員:

事實(shí)上從名字上你也能看出來(lái)這每一個(gè)成員代表的含義,與我們?cè)?Python 中常用的魔法方法很像。而且這里面的成員雖然多,但并非每一個(gè)類型對(duì)象都具備,比如 int 類型它就沒(méi)有 tp_as_sequence 和 tp_as_mapping,所以 int 類型的這兩個(gè)成員的值都是 0。綜上所述,Python 底層通過(guò) PyObject 和 PyTypeObject 完成了 C++ 所提供的對(duì)象的多態(tài)特性。在 Python 中創(chuàng)建一個(gè)對(duì)象,會(huì)分配內(nèi)存并進(jìn)行初始化,然后 Python 會(huì)用一個(gè) PyObject * 來(lái)保存和維護(hù)這個(gè)對(duì)象,因此在 Python 中,變量的傳遞(包括函數(shù)的參數(shù)傳遞)實(shí)際上傳遞的都是一個(gè)泛型指針:PyObject *。這個(gè)指針具體指向什么類型的對(duì)象我們并不知道,只能通過(guò)其內(nèi)部的 ob_type 成員進(jìn)行動(dòng)態(tài)判斷,而正是因?yàn)檫@個(gè) ob_type,Python 實(shí)現(xiàn)了多態(tài)機(jī)制。以變量 a + b 為例,這個(gè) a 和 b 指向的對(duì)象可以是整數(shù)、浮點(diǎn)數(shù)、字符串、列表、元組、甚至是我們自己實(shí)現(xiàn)了 add 方法的類的實(shí)例對(duì)象。因?yàn)槲覀冋f(shuō) Python 中的變量都是一個(gè) PyObject *,所以它可以指向任意的對(duì)象,因此 Python 就無(wú)法做基于類型方面的優(yōu)化。首先 Python 底層要通過(guò) ob_type 判斷變量指向的對(duì)象到底是什么類型,這在 C 的層面上至少需要一次屬性查找。然后 Python 將每一個(gè)操作都抽象成了一個(gè)魔法方法,所以實(shí)例相加時(shí)要在類型對(duì)象中找到該方法對(duì)應(yīng)的函數(shù)指針,這又是一次屬性查找。找到了之后將 a、b 作為參數(shù)傳遞進(jìn)去,這會(huì)發(fā)生一次函數(shù)調(diào)用,會(huì)將對(duì)象維護(hù)的值拿出來(lái)進(jìn)行運(yùn)算,然后根據(jù)相加的結(jié)果創(chuàng)建一個(gè)新的對(duì)象,再返回其對(duì)應(yīng)的 PyObject * 指針。而對(duì)于 C 來(lái)講,由于已經(jīng)規(guī)定好了類型,所以 a + b 在編譯之后就是一條簡(jiǎn)單的機(jī)器指令,因此兩者在效率上差別很大。

(三)對(duì)象的創(chuàng)建與調(diào)用

拋出個(gè)問(wèn)題: item = 2.71 和 item = float(2.71) 得到的結(jié)果都是2.71,但它們之間有什么不同呢?;蛘哒f(shuō)列表: lst = [] 和 lst = list()得到的 lst 也都是一個(gè)空列表,但這兩種方式有什么區(qū)別呢?Python 中有許多效果相同,過(guò)程不同的表達(dá),值得我們進(jìn)一步思考。

事實(shí)上,Python 內(nèi)部創(chuàng)建一個(gè)對(duì)象的方法有兩種:

? 通過(guò) Python/C API,可以是泛型API、也可以是特型API,用于內(nèi)置類型

? 通過(guò)對(duì)應(yīng)的類型對(duì)象去創(chuàng)建,多用于自定義類型Python 對(duì)外提供了 C API,讓用戶可以從 C 環(huán)境中與其交互。由于 Python 解釋器是用 C 寫(xiě)成的,所以 Python 內(nèi)部也在大量使用這些 C API。為了更好的研讀源碼,系統(tǒng)地了解這些 API 的組成結(jié)構(gòu)是很有必要的,下面以 PyFloatObject 對(duì)象為例,通過(guò)源碼的大致步驟了解它的兩種創(chuàng)建過(guò)程。首先先看浮點(diǎn)數(shù)的定義:

可以看出,PyFloatObject 的結(jié)構(gòu)非常簡(jiǎn)單,除了 PyObject 這個(gè)公共的頭部信息之外,只有一個(gè)額外的 ob_fval,用于存儲(chǔ)具體的值,并且使用的是 C 中的 double。以 f = 3.14 為例,底層結(jié)構(gòu)如下:

使用泛型 API 創(chuàng)建



使用特型 API 創(chuàng)建

綜上,不管采用哪種方式創(chuàng)建,最終的關(guān)鍵步驟都是分配內(nèi)存,創(chuàng)建內(nèi)置類型的實(shí)例對(duì)象,Python 是可以直接分配內(nèi)存的。因?yàn)樗鼈冇心男┏蓡T在底層都是寫(xiě)死的,Python 對(duì)它們了如指掌,因此可以通過(guò) Python/C API 直接分配內(nèi)存并初始化。以 PyFloat_FromDouble 為例,直接在接口內(nèi)部為 PyFloatObject 結(jié)構(gòu)體實(shí)例分配內(nèi)存,并初始化相關(guān)字段即可。從下文的實(shí)驗(yàn)也可以看出,對(duì)于內(nèi)置類型的實(shí)例對(duì)象而言,使用 Python / C API 創(chuàng)建要快不少。

比如創(chuàng)建列表:可以使用 list()、也可以使用 [ ];創(chuàng)建元組:可以使用 tuple()、也可以使用 ();創(chuàng)建字典:可以使用 dict()、也可以使用 {}。前者是通過(guò)類型對(duì)象去創(chuàng)建的,后者是通過(guò) Python/C API 創(chuàng)建。但對(duì)于內(nèi)置類型而言,我們推薦使用 Python/C API 創(chuàng)建,會(huì)直接解析為對(duì)應(yīng)的 C 一級(jí)數(shù)據(jù)結(jié)構(gòu),因?yàn)檫@些結(jié)構(gòu)在底層都是已經(jīng)實(shí)現(xiàn)好了的,是可以直接用的,無(wú)需通過(guò)諸如 list() 這種調(diào)用類型對(duì)象的方式來(lái)創(chuàng)建,因?yàn)樗鼈儍?nèi)部還是使用了 Python/C API。

? 五、總結(jié)? ? ?

Python是一門(mén)備受推崇的腳本語(yǔ)言,以其簡(jiǎn)單的語(yǔ)法和全面的功能而著稱,可快速實(shí)現(xiàn)各種業(yè)務(wù)。本文從 CPython 對(duì)象構(gòu)造器入手,介紹了浮點(diǎn)數(shù)對(duì)象在 CPython 底層數(shù)據(jù)結(jié)構(gòu)中的表現(xiàn)形式以及對(duì)象創(chuàng)建的過(guò)程。通過(guò)進(jìn)一步了解 CPython 動(dòng)態(tài)性的實(shí)現(xiàn)方式,讀者可望在閱讀 CPython 源碼后提升編寫(xiě)高質(zhì)量代碼的能力。參考資料:

  1. https://github.com/python/cpython
  2. https://docs.python.org/zh-cn/3.11/c-api/index.html
  3. https://jiuaidu.com/jianzhan/990904/
  4. https://www.ab62.cn/article/15965.html
  5. https://zhuanlan.zhihu.com/p/596637636