Python 的任何 variable 都是 C++ 的 Object,所以我們在對 Python 的 variable 做任何操作時,其實就是在對 Object 做。例如,我們做 variable 比較時:
1 | if a == b: |
variable 複製:
1 | a = [1, 2, 3] |
可是如果只是”賦予”新的 variable,例如:
1 | a = [1, 2, 3] |
b
會影響 a
,因為它們共享同個記憶體上的 address。不過透過上面的範例,你可能還是不太清楚:
a == b
是比較兩個 object address 相等呢?還是 value 相等呢?b = list(a)
是 shallow copy 還是 deep copy 呢?
比較語法
在 Python 中你應該遇過下面兩個語法,你能分辨出有什麼不同嗎?
1 | if a == b: |
==
表示兩個 variables 的”值”是否相等,is
表示兩個 variables 是否為同一個 Object,是否 address 也相同。在 Python 中,我們可以透過 id(variable)
去拿 variable 的唯一 ID,所以判斷 a is b
如同 id(a) == id(b)
。我們再看一個百思不得其解的例子:
1 | a = 10 |
咦?a
和 b
是兩個不同的變數呀,為何它們的 id 會相同呢?我們說過,Python 的任何 variable 都是 C++ 的 Object,int 也不例外。然而,為了提升性能,C++ 把常用的數字 -5 到 256 先定義好,作為 cache 使用,當 Python 需要時,直接從這個 pool 拿去引用。於是乎:
1 | a = 257 |
有興趣的讀者可以試試。再來,我們來探討 ==
和 is
性能的部分。通常,is
會比 ==
快很多,因為 is
不會被 overload,這樣 Python 就不需要去尋找 __eq__
,a == b
實際上做的事情等同於 a.__eq__(b)
。
copy 語法
所謂的 copy,指的是重新分配一塊記憶體,創建一個新的 Object,所以它們的 id 肯定是不同的。而 copy 又分 shallow copy 和 deep copy。所謂的 shallow copy,新的 Object 裡面的元素是原本 Object 裡面元素的引用,所以如果 Object 裡面的 Object 被改了,新舊 Object 都會被連動。例如:
1 | a = [1, [1, 2, 3]] |
可以看到 variable b
append 4 之後,a
裡面的 list 也被影響了。而 deep copy,相對於 shallow copy 來說,會遞迴的方式往裡面一直 copy,所以新的 Object 和舊的 Object 沒有任何關聯。Python 中透過 copy.deepcopy(object)
實現 deep copy,比如:
1 | import copy |
我們可以看到 a
不受任何影響即使 b
append 了 4。最後我們再來探討 mutable (可變) Object 和 immutable (不可變) Object,看一下下面的例子:
1 | a = 1000 |
我們可以看到,a += 1
後竟然沒有影響 b
,不是說 Python 一切 variable 皆是 object 嗎?這是因為在 Python 中 int, float, string, tuple 等屬於 immutable object,不能改 object 裡面的值。我們可以看到 a += 1
之後,a
的 id 變了,代表它被重新賦予新的 object:
1 | a = 1000 |
而 dict, array, set 等等,屬於 mutable object,在 assign 或是當作參數傳進去 function 的時候,會影響該 variable。
總結
這篇文章講了 Python 的 variable 怎麼做比較和複製:
- Python 的一切皆為 Object,所以比較和複製的思維可以參考 C++
- Object 間的比較分成 address 和 value 的比較,分別為
is
和==
- Object 間的複製分成 shallow 和 deep copy,一個只複製了第一層的 value,另一個會遞迴複製。