關於 Python 列表解析式的作用域問題

列表解析式應該是工作中經常使用的一個技巧了,嚴格意義上講甚至都不算是一個技巧。但列表解析的背後卻隱藏着一些坑,一不小心就會造成難以察覺的 bug,舉個例子:

x = 1

class Girl:
    x = 2
    print([i * x for i in (1, 2, 3)])

你覺得這段代碼執行之後會打印什麼呢?可能有人覺得是 [2, 4, 6],其實答案是 [1, 2, 3]。事實上如果全局作用中沒有 x 的話,或者我們把外部的 x = 1 給刪掉,那麼是會報錯的。

原因是 Python 的列表解析式具有獨立的作用域,我們知道 Python 在變量查找的時候遵循 LEGB 規則,也就是按照本地作用域、閉包、全局作用域、內置作用域的順序進行查找。

而列表解析式具有自己獨立的作用域,也就是內部在查找變量 x 時的本地作用域。但顯然當前列表解析式內部並沒有定義 x 這個變量,於是會從閉包裏面查找,但這裏也沒有閉包,因此會從全局作用域中查找,發現 x 等於 1。

再舉個例子:

age = 20
# 在列表解析的時候
# 我們分別將 15、16、17 賦值給了變量 age
data = [age + 1 for age in (15, 16, 17)]
# 但列表解析具有獨立的作用域
# 因此兩個 age 是無關的
print(age)  # 20

# 如果改成普通的 for 循環
age = 20
for age in (15, 16, 17):
    pass
# 循環結束之後,age 變成了 17
# 因爲這兩個 age 位於同一個作用域
# 所以 age 最終會指向 17
print(age)  # 17

當然啦,這些都屬於非常基礎的內容了,要是因爲這種問題而被坑了,那隻能說明我太蠢了。我被坑的原因是,列表解析式具有獨立的作用域這一結論在 Python3 當中是成立的,但在 Python2 當中不成立。

相同的代碼,但因爲 Python 版本差異而導致執行的結果不同。而當時我的項目因爲歷史原因是跑在 Python2 上面的,所以就被這個問題給坑了。

以上就是本文的內容(好水的一篇文章,一直猶豫要不要發),因爲覺得這個問題比較有意思,於是拿出來分享一下。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/fPmo14sBZXJgEUodTL7y1g