資深程序員:Python中這4個引用,你是否有為之迷惑過呢?


資深程序員:Python中這4個引用,你是否有為之迷惑過呢?


第一個:執行時機的差異

1.

資深程序員:Python中這4個引用,你是否有為之迷惑過呢?

Output:

資深程序員:Python中這4個引用,你是否有為之迷惑過呢?

2.

資深程序員:Python中這4個引用,你是否有為之迷惑過呢?

Output:

資深程序員:Python中這4個引用,你是否有為之迷惑過呢?

說明

  • 在生成器表達式中, in 子句在聲明時執行, 而條件子句則是在運行時執行.
  • 所以在運行前, array 已經被重新賦值為 [2, 8, 22], 因此對於之前的 1, 8 和 15, 只有 count(8) 的結果是大於 0的, 所以生成器只會生成 8.
  • 第二部分中 g1 和 g2 的輸出差異則是由於變量 array_1 和 array_2 被重新賦值的方式導致的.
  • 在第一種情況下, array_1 被綁定到新對象 [1,2,3,4,5], 因為 in 子句是在聲明時被執行的, 所以它仍然引用舊對象 [1,2,3,4](並沒有被銷燬).
  • 在第二種情況下, 對 array_2 的切片賦值將相同的舊對象 [1,2,3,4] 原地更新為 [1,2,3,4,5]. 因此 g2 和 array_2 仍然引用同一個對象(這個對象現在已經更新為 [1,2,3,4,5]).

第二個:出人意料的is

下面是一個在互聯網上非常有名的例子.

資深程序員:Python中這4個引用,你是否有為之迷惑過呢?

說明:

is 和 == 的區別

  • is 運算符檢查兩個運算對象是否引用自同一對象 (即, 它檢查兩個運算對象是否相同).
  • == 運算符比較兩個運算對象的值是否相等.因此 is 代表引用相同, == 代表值相等. 下面的例子可以很好的說明這點,
資深程序員:Python中這4個引用,你是否有為之迷惑過呢?

256 是一個已經存在的對象, 而 257 不是

當你啟動Python 的時候, 數值為 -5 到 256 的對象就已經被分配好了. 這些數字因為經常被使用, 所以會被提前準備好.

Python 通過這種創建小整數池的方式來避免小整數頻繁的申請和銷燬內存空間.

當前的實現為-5到256之間的所有整數保留一個整數對象數組, 當你創建了一個該範圍內的整數時, 你只需要返回現有對象的引用. 所以改變1的值是有可能的. 我懷疑這種行為在Python中是未定義行為. :-)

資深程序員:Python中這4個引用,你是否有為之迷惑過呢?

這裡解釋器並沒有智能到能在執行 y = 257 時意識到我們已經創建了一個整數 257, 所以它在內存中又新建了另一個對象.

當 a 和 b 在同一行中使用相同的值初始化時,會指向同一個對象.

資深程序員:Python中這4個引用,你是否有為之迷惑過呢?


  • 當 a 和 b 在同一行中被設置為 257 時, Python 解釋器會創建一個新對象, 然後同時引用第二個變量. 如果你在不同的行上進行, 它就不會 "知道" 已經存在一個 257 對象了.
  • 這是一種特別為交互式環境做的編譯器優化. 當你在實時解釋器中輸入兩行的時候, 他們會單獨編譯, 因此也會單獨進行優化. 如果你在 .py 文件中嘗試這個例子, 則不會看到相同的行為, 因為文件是一次性編譯的.

第三個:影子數組

資深程序員:Python中這4個引用,你是否有為之迷惑過呢?

Output:

資深程序員:Python中這4個引用,你是否有為之迷惑過呢?

我們有沒有賦值過3個 "X" 呢?

說明:

當我們初始化 row 變量時, 下面這張圖展示了內存中的情況。

資深程序員:Python中這4個引用,你是否有為之迷惑過呢?

而當通過對 row 做乘法來初始化 board 時, 內存中的情況則如下圖所示 (每個元素 board[0], board[1] 和 board[2] 都和 row 一樣引用了同一列表.)

資深程序員:Python中這4個引用,你是否有為之迷惑過呢?

我們可以通過不使用變量 row 生成 board 來避免這種情況. (這個issue提出了這個需求.)

資深程序員:Python中這4個引用,你是否有為之迷惑過呢?

第四個:混亂的輸出

資深程序員:Python中這4個引用,你是否有為之迷惑過呢?

Output:

資深程序員:Python中這4個引用,你是否有為之迷惑過呢?

即使每次在迭代中將 some_func 加入 funcs 前的 x 值都不相同, 所有的函數還是都返回6.

再換個例子

資深程序員:Python中這4個引用,你是否有為之迷惑過呢?

說明:

  • 當在循環內部定義一個函數時, 如果該函數在其主體中使用了循環變量, 則閉包函數將與循環變量綁定, 而不是它的值. 因此, 所有的函數都是使用最後分配給變量的值來進行計算的.
  • 可以通過將循環變量作為命名變量傳遞給函數來獲得預期的結果. 為什麼這樣可行? 因為這會在函數內再次定義一個局部變量.
資深程序員:Python中這4個引用,你是否有為之迷惑過呢?

Output:

資深程序員:Python中這4個引用,你是否有為之迷惑過呢?


分享到:


相關文章: