教你閱讀 Cpython 的源碼(一)

原文:https://realpython.com/cpython-source-code-guidepython

譯者:陳祥安。c++

目錄

第一部分-介紹 Cpython

源代碼中有什麼? 如何編譯 Cpython 代碼 編譯器能作什麼? 爲何 Cpython 是用 C 語言而是 Python 編寫的? Python 語言的規範 Cpython 中的內存管理機制 結論git

第二部分-Python 解釋器進程

創建運行時配置 讀取文件/輸入 詞法解析和句法解析 抽象語法樹 結論程序員

第三部分- Cpython 的編譯器和執行循環

編譯 執行 結論github

第四部分-Cpython 中的對象

基礎對象類型 Bool 和 Long Integer 類型 回顧 Generator 類型 結論正則表達式

第五部分 Cpython 標準庫

Python 模塊 Pyhton 和 C 模塊 Cpython 迴歸測試套件 安裝用戶自定 C 義版本算法

最後-Cpython 源代碼:結論

···-------------------------正文開始----------------------------···sql

第一部分 介紹 Cpython

前言

在使用 Python 的過程當中你是否有這些疑惑,使用字典查找內容,爲何比遍歷一個列表要快得多?生成器如何在每次生成值時記住變量的狀態?爲何使用 Python 時咱們不用像其餘語言那樣分配內存?事實證實,CPython,是最流行的 Python 版本,運行時是用人類可讀的 C 和 Python 代碼編寫的。express

這篇文主要就是圍繞着 Cpython 展開的,文章涵蓋 CPython 內部原理背後的全部概念、它們的工做原理以及可視化的解釋。 你將會學到的內容有:編程

  • 學會閱讀源碼
  • 從源代碼編譯 CPython
  • 理解列表、字典和生成器等概念以及內部工做原理
  • 運行測試套件
  • 修改或升級 CPython 庫的組件,或許在將來能夠貢獻 新的 Python 版本

這篇文章很長可是頗有用,若是你決定要學習 Cpython,那麼但願你能看下去,你會發現這是一份不錯的學習資料。 這篇文章總共分爲 5 部分,你能夠根據本身的時候合理的安排閱讀時間。每一部分都要花必定的時間,經過本身去研究這裏面的一些案例,你會感到一種成就感,由於你掌握了 Python 的核心概念,這使得你成爲一名更好的 Python 程序員。 咱們平時說的 Python,其實大多都是指的 Cpython,CPython 是衆多 Python 中的其中一種,除此以外還有 Pypy,Jpython 等。CPython 一樣的做爲官方使用的 Python 版本,以及網上的衆多案例。因此,這裏咱們主要說的是 Cpython。 注意:本文是針對 CPython 源代碼的 3.8.0b3 版編寫的。

源代碼中有什麼?

CPython 源代碼分發包含各類工具,庫和組件。咱們將在本文中探討這些內容。 首先,咱們將重點關注編譯器。先從 git 上下載 Cpython 源代碼.

git clone https://github.com/python/cpython
cd cpython
git checkout v3.8.0b3 #切換咱們須要的分支

注意:若是你沒有 Git,能夠直接從 GitHub 網站下載 ZIP 文件中的源代碼。 解壓咱們下載的文件,其目錄結構以下:

cpython/
│
├── Doc      ← 源代碼文檔說明
├── Grammar  ← 計算機可讀的語言定義
├── Include  ← C 語言頭文件(頭文件中通常放一些重複使用的代碼)
├── Lib      ← Python 寫的標準庫文件
├── Mac      ← Mac 支持的文件
├── Misc     ← 雜項
├── Modules  ← C 寫的標準庫文件
├── Objects  ← 核心類型和對象模塊
├── Parser   ← Python 解析器源碼
├── PC       ← Windows 編譯支持的文件
├── PCbuild  ← 老版本的 Windows 系統 編譯支持的文件
├── Programs ← Python 可執行文件和其餘二進制文件的源代碼
├── Python   ← CPython  解析器源碼
└── Tools    ← 用於構建或擴展 Python 的獨立工具

接下來,咱們將從源代碼編譯 CPython。 此步驟須要 C 編譯器和一些構建工具。不一樣的系統編譯方法也不一樣,這裏我用的是 mac 系統。

在 macOS 上編譯 CPython 很是簡單。在終端內,運行如下命令便可安裝 C 編譯器和工具包:

$ xcode-select --install

此命令將彈出一個提示,下載並安裝一組工具,包括 Git,Make 和 GNU C 編譯器。 你還須要一個 OpenSSL 的工做副本,用於從 PyPi.org 網站獲取包。 若是你之後計劃使用此版原本安裝其餘軟件包,則須要進行 SSL 驗證。 在 macOS 上安裝 OpenSSL 的最簡單方法是使用 HomeBrew。 若是已經安裝了 HomeBrew,則可使用 brew install 命令安裝 CPython 的依賴項。

$ brew install openssl xz zlib

如今你已擁有依賴項,你能夠運行 Cpython 目錄下的 configure 腳本:

$ CPPFLAGS="-I$(brew --prefix zlib)/include" \
 LDFLAGS="-L$(brew --prefix zlib)/lib" \
 ./configure --with-openssl=$(brew --prefix openssl) --with-pydebug

上面的安裝命令中, CPPFLAGS 是 c 和 c++ 編譯器的選項,這裏指定了 zlib 頭文件的位置, LDFLAGS 是 gcc 等編譯器會用到的一些優化參數,這裏是指定了 zlib 庫文件的位置, (brew --prefix openssl) 這一部分的意思是在終端裏執行括號裏的命令,顯示 openssl 的安裝路徑,能夠事先執行括號裏的命令,用返回的結果替換 (brew --prefix openssl),效果是同樣的,每一行行尾的反斜槓可使換行時先不執行命令,而是把這三行內容看成一條命令執行。

運行完上面命令之後在存儲庫的根目錄中會生成一個 Makefile,你可使用它來自動化構建過程。./configure步驟只須要運行一次。 你能夠經過運行如下命令來構建 CPython 二進制文件。

$ make -j2 -s

-j2 標誌容許 make 同時運行 2 個做業。若是你有 4 個內核,則能夠將其更改成 4. -s 標誌會阻止 Makefile 將其運行的每一個命令打印到控制檯。你能夠刪除它,輸出的東西太多了。在構建期間,你可能會收到一些錯誤,在摘要中,它會通知你並不是全部包均可以構建。 例如,_dbm,_sqlite3,_uuid,nis,ossaudiodev,spwd 和_tkinter 將沒法使用這組指令構建。若是你不打算針對這些軟件包進行開發,這個錯誤沒什麼影響。若是你實在須要能夠參考:https://devguide.python.org/。 構建將花費幾分鐘並生成一個名爲 python.exe 的二進制文件。每次改動源代碼,都須要從新運行 make 進行編譯。 python.exe 二進制文件是 CPython 的調試二進制文件。執行下面命令能夠看到 Python 的運行版本。

$ ./python.exe
Python 3.8.0b3 (tags/v3.8.0b3:4336222407, Aug 21 2019, 10:00:03) 
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

(其實最新的已經到 Python3.9 了,我編譯了一下效果以下)

編譯器作了什麼?

編譯器的目的就是將一種語言轉爲另一種語言。能夠把編譯的過程比做翻譯,把英語裏的「Hello」,翻譯成中文的「你好」。

一些編譯器將代碼編譯成只有機器看懂的機器代碼,能夠直接在系統上進行執行。其餘編譯器將編譯成中間語言,由虛擬機執行。 選擇編譯器時作出的一個重要決定是系統可移植性要求。Java 和.NET CLR 將編譯成中間語言,以便編譯的代碼可適配其餘系統類型。C,Go,C ++和 Pascal 將編譯成一個低級可執行文件,只能在相似於編譯的系統上運行。 咱們通常會直接發佈 Python 的源代碼,而後直接經過 Python 命令便可運行,其實在內部,運行時 CPython 會編譯你的代碼。大多數認爲 Python 是一種解釋性語言。 嚴格來講其實它其實是編譯類型。

Python 代碼不會編譯成機器代碼。 它被編譯成一種特殊的低級中間語言,只有 CPython 才能理解的字節碼。在 Python3 中字節碼就存儲在隱藏目錄中的.pyc 文件中,提供了緩存以供下次快速執行。因此,若是在不更改源代碼的狀況下運行相同的 Python 應用程序兩次,第二次老是會快得多。緣由就是第二次的時候直接加載了字節碼而後運行了程序,不像第一次還須要編譯。

爲何 CPython 是用 C 而不是 Python 編寫的?

CPython 中的 C 是對 C 編程語言的引用,暗示這個 Python 發行版是用 C 語言編寫的。 CPython 中的編譯器是用純 C 編寫的。可是,許多標準庫模塊都是用純 Python 或 C 和 Python 的組合編寫的。

那麼爲何 CPython 是用 C 而不是 Python 編寫的?

答案就在於編譯器的工做原理。 編譯器有兩種類型:

  • 自託管編譯器是用它們編譯的語言編寫的編譯器,例如 Go 編譯器。
  • 源到源編譯器是用另外一種已經有編譯器的語言編寫的編譯器。 這也就意味着若是從頭開始編寫新的編程語言,則須要一個可執行的應用程序來編譯你的編譯器!你就須要一個編譯器來執行任何操做,所以在開發新語言時,它們一般首先用較舊的,更成熟的語言編寫。同時節省時間和學習成本。 一個很好的例子就是 Go 語言。 第一個 Go 編譯器是用 C 編寫的,而後 Go 能夠編譯,編譯器就在 Go 中重寫了。

CPython 保留了它的 C 的特性:許多標準庫模塊(如 ssl 模塊或 sockets 模塊)都是用 C 語言編寫的,用於訪問低級操做系統 API。 用於建立網絡套接字,與文件系統一塊兒工做或與顯示器交互的 Windows 和 Linux 內核中的 API 都是用 C 語言編寫的。因此將 Python 的可擴展性層專一於 C 語言是有意義的。在本文的後面部分,咱們將介紹 Python 標準庫和 C 模塊。除此 以外,有一個用 Python 編寫的 Python 編譯器叫作 PyPy。 PyPy 的徽標是一個 Ouroboros,表明編譯器的自託管特性。另外一個 Python 交叉編譯器的例子是 Jython。

還有一個就是 Jython。Jython 是用 Java 編寫的,從 Python 源代碼編譯成 Java 字節碼。與 CPython 能夠輕鬆導入 C 庫並從 Python 中使用它們同樣,Jython 使得導入和引用 Java 模塊和類變得容易。

Python 語言規範

CPython 源代碼中包含的是 Python 語言的定義。這是全部 Python 解釋器使用的參考規範。該規範採用人類可讀和機器可讀的格式。文檔內部詳細說明了 Python 語言,容許的內容以及每一個語句的行爲方式。

文檔

位於Doc/reference目錄內的是reStructuredText文件解釋了 Python 語言中每一個功能屬性。這構成了docs.python.org上的官方 Python 參考指南。 在目錄中是你須要瞭解整個語言,結構和關鍵字的文件:

cpython/Doc/reference
|
├── compound_stmts.rst
├── datamodel.rst
├── executionmodel.rst
├── expressions.rst
├── grammar.rst
├── import.rst
├── index.rst
├── introduction.rst
├── lexical_analysis.rst
├── simple_stmts.rst
└── toplevel_components.rst

compound_stmts.rst文件中,你能夠看到一個定義 with 語句的簡單示例。with 語句能夠在 Python 中以多種方式使用,最簡單的是上下文管理器的實例化和嵌套的代碼塊:

with x():
   ...

你可使用 as 進行重命名

with x() as y:
   ...

你還能夠鏈式的同時定義多個

with x() as y, z() as jk:
   ...

接下來,咱們將探索 Python 語言的計算機可讀文檔。

Grammar

該文檔包含人類可讀規範和存放在單個文件Grammar/Grammar中的機器可讀規範。 Grammar 文件是使用稱爲 Backus-Naur Form(BNF)的上下文表示法進行編寫的。 BNF 不是特定於 Python 的,而且一般用做許多其餘語言中語法的符號。 編程語言中的語法結構概念是從 20 世紀 50 年代Noam Chomsky’s work on Syntactic Structures 中受到啓發的。 Python 的語法文件使用具備正則表達式語法的 Extended-BNF(EBNF)規範。 因此,在語法文件中你可使用:

  • *重複
  • +至少重複一次
  • []爲可選部分
  • |任選一個
  • ()用於分組 若是在語法文件中搜索 with 語句,你將看到 with 語句的定義:
.. productionlist::
   with_stmt: "with" `with_item` ("," `with_item`)* ":" `suite`
   with_item: `expression` ["as" `target`]

引號中的內容都是字符串,這是一中關鍵字的定義方式。因此 with_stmt 指定爲: 1.with單詞開頭 2.接下來是 with_item,它是一個test和(可選)as 表達式。 3.多個項目之間使用逗號進行間隔 4.以字符:結尾 5.其次是 suite。 這兩行中提到了一些其餘定義:

  • suite是指具備一個或多個語句的代碼塊。
  • test是指一個被評估的簡單語句。
  • expr指的是一個簡單的表達式 若是你想詳細探索這些內容,能夠在此文件中定義整個 Python 語法。

若是你想看一個最近如何使用語法的例子,例如在 PEP572 中,:=運算符被添加到語法文件中。

ATEQUAL                 '@='
  RARROW                  '->'
  ELLIPSIS                '...'
+ COLONEQUAL              ':='
  OP
  ERRORTOKEN

使用 pgen

Grammar 文件自己不會被 Python 編譯器使用。 是使用一個名爲 pgen 的工具,來建立的解析器表。pgen 會讀取語法文件並將其轉換爲解析器表。若是你對語法文件進行了更改,則必須從新生成解析器表並從新編譯 Python。

注意:pgen 應用程序在 Python 3.8 中從 C 重寫爲純 Python。

爲了查看 pgen 的運行狀況,讓咱們改變 Python 語法的一部分。並從新編譯運行 Python。 在 Grammar 路徑下看到兩個文件 Grammar 和 Tokens,咱們在 Grammar 搜索 pass_stmt,而後看到下面這樣

pass_stmt: 'pass'

咱們修改一下,改成下面這樣

pass_stmt: 'pass' | 'proceed'

在 Cpython 的根目錄使用 make regen-grammar命令來運行pgen從新編譯 Grammar 文件。 應該看到相似於此的輸出,代表已生成新的Include/graminit.hPython/graminit.c文件: 下面是部分輸出內容

# Regenerate Include/graminit.h and Python/graminit.c
# from Grammar/Grammar using pgen
PYTHONPATH=. python3 -m Parser.pgen ./Grammar/Grammar \
		./Grammar/Tokens \
		./Include/graminit.h.new \
		./Python/graminit.c.new
python3 ./Tools/scripts/update_file.py ./Include/graminit.h ./Include/graminit.h.new
python3 ./Tools/scripts/update_file.py ./Python/graminit.c ./Python/graminit.c.new

使用從新生成的解析器表,須要從新編譯 CPython 才能查看新語法。使用以前用於操做系統的相同編譯步驟。

make -j4 -s

若是代碼編譯成功,執行新的 CPython 二進制文件並啓動 REPL。

./python.exe

在 REPL 中,如今能夠嘗試定義一個函數,使用編譯爲 Python 語法的 proceed 關鍵字替代 pass 語句。

Python 3.8.0b3 (tags/v3.8.0b3:4336222407, Aug 21 2019, 10:00:03) 
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def example():
...    proceed
... 
>>> example()

下面是我運行結果,頗有意思竟然沒有出錯。 接下來,咱們將探討 Tokens 文件及其與 Grammar 的關係。

Tokens

與 Grammar 文件夾中的語法文件一塊兒是一個 Tokens 文件,它包含在解析樹中做爲葉節點找到的每一個惟一類型,稍後咱們將深刻介紹解析器樹。每一個 token 還具備名稱和生成的惟一 ID,這些名稱用於簡化在 tokenizer 中引用。

注意:Tokens 文件是 Python 3.8 中的一項新功能。

例如,左括號稱爲 LPAR,分號稱爲 SEMI。 你將在本文後面看到這些標記:

LPAR                    '('
RPAR                    ')'
LSQB                    '['
RSQB                    ']'
COLON                   ':'
COMMA                   ','
SEMI                    ';'

與語法文件同樣,若是更改 Tokens 文件,則須要再次運行 pgen。 要查看操做中的 tokens,能夠在 CPython 中使用 tokenize 模塊。建立一個名爲 test_tokens.py 的簡單 Python 腳本:

# Hello world!
def my_function():
   proceed

而後經過名爲 tokenize 的標準庫中內置的模塊傳遞此文件。你將按行和字符查看令牌列表。使用-e 標誌輸出確切的令牌名稱:

0,0-0,0:            ENCODING       'utf-8'        
1,0-1,14:           COMMENT        '# Hello world!'
1,14-1,15:          NL             '\n'           
2,0-2,3:            NAME           'def'          
2,4-2,15:           NAME           'my_function'  
2,15-2,16:          LPAR           '('            
2,16-2,17:          RPAR           ')'            
2,17-2,18:          COLON          ':'            
2,18-2,19:          NEWLINE        '\n'           
3,0-3,3:            INDENT         '   '          
3,3-3,7:            NAME           'proceed'         
3,7-3,8:            NEWLINE        '\n'           
4,0-4,0:            DEDENT         ''             
4,0-4,0:            ENDMARKER      ''

在輸出中,第一列是行/列座標的範圍,第二列是令牌的名稱,最後一列是令牌的值。 在輸出中,tokenize 模塊隱含了一些不在文件中的標記。 utf-8 的 ENCODING 標記,末尾有一個空行,DEDENT 關閉函數聲明,ENDMARKER 結束文件。tokenize 模塊是用純 Python 編寫的,位於 CPython 源代碼中的Lib/tokenize.py中。

重要提示:CPython 源代碼中有兩個 tokenizers:一個用 Python 編寫,上面演示的這個,另外一個是用 C 語言編寫的。用 Python 編寫的被用做實用程序,而用 C 編寫的被用於 Python 編譯器。可是,它們具備相同的輸出和行爲。用 C 語言編寫的版本是爲性能而設計的,Python 中的模塊是爲調試而設計的。

要查看 C 語言的的 tokenizer 的詳細內容,可使用-d 標誌運行 Python。 使用以前建立的 test_tokens.py 腳本,使用如下命令運行它:

./python.exe -d test_tokens.py

獲得以下結果

Token NAME/'def' ... It's a keyword
 DFA 'file_input', state 0: Push 'stmt'
 DFA 'stmt', state 0: Push 'compound_stmt'
 DFA 'compound_stmt', state 0: Push 'funcdef'
 DFA 'funcdef', state 0: Shift.
Token NAME/'my_function' ... It's a token we know
 DFA 'funcdef', state 1: Shift.
Token LPAR/'(' ... It's a token we know
 DFA 'funcdef', state 2: Push 'parameters'
 DFA 'parameters', state 0: Shift.
Token RPAR/')' ... It's a token we know
 DFA 'parameters', state 1: Shift.
  DFA 'parameters', state 2: Direct pop.
Token COLON/':' ... It's a token we know
 DFA 'funcdef', state 3: Shift.
Token NEWLINE/'' ... It's a token we know
 DFA 'funcdef', state 5: [switch func_body_suite to suite] Push 'suite'
 DFA 'suite', state 0: Shift.
Token INDENT/'' ... It's a token we know
 DFA 'suite', state 1: Shift.
Token NAME/'proceed' ... It's a keyword
 DFA 'suite', state 3: Push 'stmt'
...
  ACCEPT.

在輸出中,您能夠看到它突出顯示爲關鍵字。在下一章中,咱們將看到如何執行 Python 二進制文件到達 tokenizer 以及從那裏執行代碼會發生什麼。如今您已經概述了 Python 語法以及 tokens 和語句之間的關係,有一種方法能夠將 pgen 輸出轉換爲交互式圖形。 如下是 Python 3.8a2 語法的屏幕截圖: 看不清不要緊,用於生成此圖的 Python 包(instaviz)將在後面的章節中介紹。這裏先作了解。

Python 中的內存管理

在本文中,你將看到對 PyArena 對象的引用。

arena是 CPython 的內存管理結構之一。代碼在Python/pyarena.c中其中包含了 C 的內存分配和解除分配的方法。

在編寫的 C 程序中,開發人員應在寫入數據以前爲數據結構分配內存。此分配將內存標記爲屬於操做系統的進程。當再也不使用已分配的內存並將其返回到操做系統的可用內存塊表時,開發人員也能夠解除分配或「釋放」它們。若是進程爲一個變量分配內存,好比在函數或循環中,當該函數完成時,內存不會自動返回給 C 中的操做系統。所以,若是它未在 C 代碼中顯式釋放,則會致使內存泄漏。每次該函數運行時,該過程將繼續佔用更多內存,直到最終,系統耗盡內存並崩潰!Python 將這一責任從程序員手中奪走,並使用兩種算法:引用計數器和垃圾收集器。每當解釋器被實例化時,PyArena方法建立並附加解釋器中的一塊內存區域。在 CPython 解釋器的生命週期中,arenas能夠被分配。它們與鏈表相關聯。

arenas將 Python 對象的指針列表存儲爲PyListObject方法。每當建立一個新的 Python 對象時,都會使用PyArena_AddPyObject方法添加指向它的指針。 此函數調用將指針存儲在arenas列表 a_objects 中。PyArena方法提供第二個功能,即分配和引用原始內存塊列表。例如,若是添加了數千個附加值,C 代碼中PyList將須要額外的內存。可是PyList不直接分配內存。該對象經過從PyObject調用具備所需內存大小的PyArena_MallocPyArena獲取原始內存塊。此任務在Objects/oballoc.c中的完成。在對象分配模塊中,能夠爲 Python 對象分配,釋放和從新分配內存。已分配塊的連接列表存儲在arenas內,所以當解釋器中止時,可使用PyArena_Free一次解除全部託管內存塊的釋放。

PyListObject爲例,若是你使用.append()一將個對象放到 Python 列表的末尾,就不須要從新分配內存了,而是使用現有列表中內存。 .append()方法調用list_resize()來處理列表的內存分配。每一個列表對象都保留已分配內存量的列表。若是要追加的項目將適合現有的可用內存,則只需添加便可。若是列表須要更多內存空間,則會進行擴展。列表的長度擴展爲 0,4,8,16,25,35,46,58,72,88。

調用PyMem_Realloc能夠擴展列表中分配的內存。 PyMem_Reallocpymalloc_realloc的 API 包裝器。Python 還有一個 C 調用malloc的特殊包裝器,它設置內存分配的最大大小以幫助防止緩衝區溢出錯誤(參見 PyMem_RawMalloc)。 綜上所述:

  • 原始內存塊的分配是經過PyMem_RawAlloc完成的。
  • Python 對象的指針存儲在PyArena中。
  • PyArena還存儲了已分配內存塊的鏈表。 有關 API 的更多信息,請參閱 CPython 文檔。

引用計數

要在 Python 中建立變量並賦值,變量名必須爲一。

my_variable = 180392

只要在 Python 中爲變量賦值,就會在 locals 和 globals 範圍內檢查變量的名稱,以查看它是否已存在。由於 my_variable 不在 locals()或 globals()字典中,因此建立了這個新對象,並將該值指定爲數字常量 180392。如今有一個對 my_variable 的引用,所以 my_variable 的引用計數器增長 1。 你能夠在 CPython 的 C 源代碼中看到函數Py_INCREFPy_DECREF。 這兩個函數分別是對該對象的遞增和遞減作引用計數。當變量超出聲明範圍時,對對象的引用會遞減。Python 中的範圍能夠指代函數或方法,生成式或 lambda 函數。這些是一些更直觀的範圍,但還有許多其餘隱式範圍,好比將變量傳遞給函數調用。遞增和遞減引用的處理在 CPython 編譯器和核心執行循環ceval.c文件中。咱們將在本文後面詳細介紹。

每當調用Py_DECREF而且計數器變爲 0 時,就會調用PyObject_Free函數。對於該對象,會爲全部已分配的內存調用PyArena_Free

垃圾收集

CPython 的垃圾收集器默認啓用,發生在後臺,用於釋放已再也不使用的對象的內存。 由於垃圾收集算法比引用計數器複雜得多,因此它不會一直髮生,不然會消耗大量的 CPU 資源。通過必定數量的操做後,它會按期發生。CPython 的標準庫附帶了一個 Python 模塊,用於與arena 和垃圾收集器 gc 模塊鏈接。 如下是在調試模式下使用 gc 模塊的方法:

>>> import gc
>>> gc.set_debug(gc.DEBUG_STATS)

這將在運行垃圾收集器時打印統計信息。 能夠經過調用get_threshold來獲取運行垃圾收集器的閾值:

>>> gc.get_threshold()
(700, 10, 10)

還能夠獲取當前閾值計數:

>>> gc.get_count()
(688, 1, 1)

最後,你能夠手動運行收集算法:

>>> gc.collect()
24

這將調用Modules/gcmodule.c文件中的collect(),該文件包含垃圾收集器算法的實現。

結論

在第 1 部分中,咱們介紹了源代碼庫的結構,如何從源代碼編譯以及 Python 語言規範。 當你深刻了解 Python 解釋器過程時,這些核心概念在第 2 部分中將是相當重要的。

更多技術內容,歡迎關注公衆號:python學習開發

相關文章
相關標籤/搜索