來讀 CPython 原始碼
什麼是 CPython?
如果這是你第一次聽到 CPython 這個名字,也許你會以為這又是一款新的 Python 實作品。不不不,CPython 不是什麼新玩具,它就是大家平常在用的那個 Python 本尊。可能有些人不知道,Python 是使用 C 語言開發出來的,所以通常講到它的時候如果沒特別聲明的話,指的就是 CPython。
為什麼要讀原始碼?
為什麼喔?沒有為什麼,就好玩而已,好玩很重要!
在看這本書的各位,如果希望透過閱讀 CPython 原始碼讓你撰寫 Python 的功力大增,那可能會讓你有些失望了。閱讀 CPython 原始碼的確會增加撰寫 Python 程式的功力,但不多。CPython 的原始碼大部份都是 C 語言寫的,所以硬要說的話,閱讀 CPython 原始碼比較可能能增進你的 C 語言的功力。
所以,如果你只是想要學習 Python 語法的話,你應該要找的是坊間的 Python 入門教學書。閱讀 CPython 的原始碼,最主要的目的是希望可以了解 Python 這個程式語言背後的黑魔法,例如大家常說的「物件」,在 Python 的物件到底是個什麼樣的東西?模組是怎麼載入的?以及 Python 的記憶體是怎麼管理的,這也是這本書主要的內容。
從哪裡開始?
取得原始碼
雖然整個 CPython 專案都可以直接在 GitHub 網站上看的到,但如果要閱讀原始碼,建議還是把整個 CPython 的原始碼下載一份到自己電腦裡慢慢研究比較有效率,下個章節就會介紹從哪裡下載。
雖然目前最新版本的 Python 是 3.13
,不過這本書所使用的 CPython 版本是 3.12.6
,不同的版本之間的原始碼可能會有些不同,特別是 Minor 版本以上的變動會差更多。所以如果各位也想要跟著我一起追原始碼的話,建議跟我使用相同版本,比較能確保我們看到一樣的內容。
另外,因為 CPython 的原始碼可能有時候會有點複雜,我會視情況省略部份程式碼,例如原本的程式碼像這樣:
struct _object {
_PyObject_HEAD_EXTRA
#if (defined(__GNUC__) || defined(__clang__)) \
&& !(defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112L)
// On C99 and older, anonymous union is a GCC and clang extension
__extension__
#endif
#ifdef _MSC_VER
// Ignore MSC warning C4201: "nonstandard extension used:
// nameless struct/union"
__pragma(warning(push))
__pragma(warning(disable: 4201))
#endif
union {
Py_ssize_t ob_refcnt;
#if SIZEOF_VOID_P > 4
PY_UINT32_T ob_refcnt_split[2];
#endif
};
#ifdef _MSC_VER
__pragma(warning(pop))
#endif
PyTypeObject *ob_type;
};
中間有好幾個 #ifdef
的條件編譯指令,有些可能跟作業系統有關,有些跟 Debug 資訊有關,這些大部份時候可能跟我們要講的重點比較沒有直接關係,所以我會視情況把它拿掉變成這樣:
struct _object {
_PyObject_HEAD_EXTRA
union {
Py_ssize_t ob_refcnt;
};
PyTypeObject *ob_type;
};
別誤會,不是那些拿掉的東西不重要,而是把這些判斷拿掉能讓重點更聚焦。
開發工具
不少開發工具都能用來追原始碼,例如 Vim、Visual Studio Code(以下簡稱 VS Code)這樣的文字編輯器,或是 Visual Studio 之類功能完整的 IDE,這些工具都有很好的原始碼閱讀功能,例如很快的跳到函數、巨集或是常數的定義或是搜尋關鍵字等等,都能幫助我們更有效率的閱讀原始碼。
在本書中我會使用 VS Code 來追原始碼,如果你還沒有安裝的話,可以到 VS Code 官網 下載安裝。
除了 VS Code 之外,最近我有個新歡叫做 Zed,這是用 Rust 開發的文字編輯器,雖然外掛不像 VS Code 那麼多,但效能比 VS Code 好很多,特別遇到專案檔案比較多或檔案比較大的時候,效能表現是肉眼可見的差別,也推薦給各位。
不會 C 語言看的懂嗎?
先說結論:就算沒有 100% 看懂也沒關係,不求甚解也無妨,閱讀原始碼的過程還是能學到不少東西。
學習並不是一直線的,不需要每行程式碼都看懂才會有所收穫,有時候只是看看原始碼的結構、函數的呼叫、變數的命名就能學到東西。就像拼圖一樣,不一定要把每塊拼圖都拼完,只要拼出個大概輪廓,也看的出來這幅拼圖長什麼樣子。
別誤會,我也不會 C 語言,幸運的是在這個世代有 ChatGPT 這些厲害的 AI 工具可以抱大腿,遇到看不懂的就丟給它們幫忙解釋一下,我也是邊看原始碼的過程一邊學 C 語言,就算是瞎子摸象,摸久後也能摸出一些東西來。
而且,整個 CPython 專案也不是全部都是用 C 語言寫的,裡面有些模組是用 Python 寫的,如果你原本就會一點 Python,也可以試著從這些模組開始看,應該會比較友善一些。但如果你是完完全全的新手,沒寫過任何一種程式語言,可能會有點難跟上進度,因此我會預期本書的讀者對程式語言有一些基本的認識,不一定要是 Python,會其它程式語言也沒關係,大概知道什麼是變數、函數、迴圈、流程控制之類的基本觀念應該差不多就夠了。
做好心準備了嗎?下個章節,我們就一起來看一下 CPython 的專案結構,順便在你的電腦編譯 CPython 原始碼,編出專屬你自己版本的 Python!