鐵人賽 - 為你自己讀 CPython 原始碼

鐵人賽 - 為你自己讀 CPython 原始碼

這次的 iThome 鐵人賽,我給自己選了一個有點硬的主題,就是閱讀 CPython 的原始碼。

今年也剛好在 PyCon Taiwan 有一場工作坊,主題是介紹如何閱讀 CPython 的原始碼,所以原本只是想藉著鐵人賽的活動,把工作坊的內容文字化做為補充資料,沒想到越寫越多,對我這個不懂 C 語言的人來說更是吃力。雖然現在有很多 AI 工具的輔助,但幾乎每篇文章得要一邊挖原始碼,一邊驗證我解讀的程式碼是不是正確的,每篇文章至少都得花三個小時以上的時間。

你是數字,我是數字,我們不一樣!

你是數字,我是數字,我們不一樣!

在 Python 裡的 id() 函數可以算出某個值或物件在 Python 世界裡的「編號」,如果有兩個值算出來的編號是一樣的,表示這兩個值不只相等,而且還是一顆物件。

那麼問題來了,我寫了一個程式叫做 hello.py,內容如下:

a = 100
b = 200

def hey():
  return 300

res1 = 300
res2 = 100 + 200
res3 = a + b
res4 = hey()

print(id(res1))
print(id(res2))
print(id(res3))
print(id(res4))

當我執行 python hello.py 的時候,最後四行印出來的 id,有一個跟其它三個是不一樣的,猜猜看是哪一個。

空虛的真

空虛的真

來一個我自己覺得滿有趣的 Python 程式問答題。

在 Python 裡,all() 這個函數可以接一個串列,串列裡的每一顆元素都成立的時候就會回傳 True,反之只要有一顆不成立,就會是 False,例如:

>>> all([True, True, True])
True
>>> all([True, False, True])
False

題目來了:

>>> all([])     # A
>>> all([[]])   # B
>>> all([[[]]]) # C

一堆中括號、小括號,眼睛都快看花了,在正式的專案千萬不要這樣整自己(或是你的同事)!大家猜猜看 A、B、C 的結果分別是什麼?

為你自己學 Python

為你自己學 Python

為你自己學 Python

https://pythonbook.cc/

TL;DR, 先說結論:

這是我最近寫的書「為你自己學 Python」,實體書 & 電子書正在編輯中,網站上的內容除另有標示外,將會以 CC BY-NC-SA 4.0 方式授權予公眾自由取用。

希望對想要學習 Python 程式語言的朋友有些幫助 :)

為什麼索引值從 0 開始算?

為什麼索引值從 0 開始算?

有沒有想過為什麼我們學的數學是從 1 開始算,但好像大部份比較主流的程式語言的陣列包括 Python 的串列,索引值卻是從 0 開始的?因為這是因為陣列的索引值並不是指向元素本身,比較像是記憶體位置的「偏移量」。

假設有一個陣列像這樣:

chars = ['a', 'b', 'c', 'd']

當我們說「變數 chars 指向 ['a', 'b', 'c', 'd'] 陣列」的時候,並不是 chars 這個變數直接指向這一整個陣列,而是 chars 變數指向這個陣列的「起始位置」,更精準的說是記憶體位置。陣列在記憶體裡的位置跟門牌號碼一樣都是連號的,假設這個陣列的起始記憶體位置是 1450,然後每個元素所佔的位置或說格子的大小我就先隨便假設是 8(先不管單位跟以及為什麼是 8)。大概示意圖如下:

為什麼索引值從 0 開始

這樣的話,你認為要怎麼取得第一個元素的 a 字元?不就是指向 1450 的位置就拿到了嗎?(你也可以說是從 14501458 這一塊空間)。

我們現在知道怎麼拿第一個元素了,接著要怎麼拿第二個?其實只要知道第一個元素的位置再搭配簡單的數學加法跟乘法就能算出來了:

起始位置 + (1 x 8) = 1458

同理可證,第三個、第四個直到第 N 個元素就能推導出計算公式:

起始位置 + (N x 8)

到這裡,大家有看出來上面公式裡的 N,剛好就是等於我們平常在講的索引值嗎?所以索引值其實就是記憶體位置的「偏移量」,因此才會從 0 開始算。

所以陣列索引值從 0 開始的概念從滿早期的程式語言開始就有了,大家用著用著也習慣了,Python 以及其它程式語言也都受到影響,所以 Python 的串列索引值也跟是從 0 開始算。

事實上,也不是所有程式語言的索引值都是從 0 開始算的,不同的程式語言對這件事有不同的觀點,像程式語言 LuaFortranCOBOLJuliaR 語言等都是從 1 開始算的,也許在他們的觀點來看,chars[1] 就是第一個或第一欄,看起來更簡單、直覺。

Python 好用的 F 字串!

Python 好用的 F 字串!

Python 裡的 F 字串(F-String)不只是可以直接把變數塞到字串裡、看起來更好看、更好寫而已,它還有一些有趣的用法,像是我們常會遇到需要幫錢錢加上千位數字的逗點,在其它程式語言可能需要透過其它函數或是另外自己寫函數來處理,用 F 字串可以輕鬆做到這件事,例如:

Python 的 Pattern Matching

Python 的 Pattern Matching

假設你曾經寫過其它的程式語言,遇到這種一大串的 else if 組合的時候,通常會想到的是 switchcase...when 之類的語法。在 Python 並沒有這樣的設計,真的遇到一堆判斷就是用 if...elif...else... 寫就是了。這樣的設計是因為 Python 的作者認為 switch 語法並不是那麼的必要,而且 if...elif...else... 也可以達到相同的效果,所以就沒有加入 switch 語法。沒有 switchcase 的設計並沒什麼大不了的,事實上有些程式語言就算有 switchcase 的寫法,本質上也只是 if..else 而已。

Python 的 String Interning 機制

Python 的 String Interning 機制

許多程式語言會使用 2 個等號 == 比較兩個值是否相等,在 Python 也是一樣,但 Python 還有個特別的比較方法 is,它可以用來比較這兩個值是不是同一個物件: