我們都知道,Python 中有兩種可變的數據類型:list 和 dict。這兩種數據類型對應的實例也有很多方法可以對自身進行修改,需要注意的是,這裡調用修改相關的方法的時候不是返回修改後的實例,而是就地修改,也就是原地修改。我們有些時候不希望原來的被修改,因此,複製它們的實例就顯得非常重要。
我們都知道,一個 list 乘上一個整數 n 表示重複 list 中的元素 n 次創建一個新 list。這裡需要注意的是 n ≥ 0,如果 n < 0,返回新的空 list(我也不知道為什麼,記就完事了),不相信可以看下面的示例。
>>> [0, 1] * 2[0, 1, 0, 1]>>> [0, 1] * 1[0, 1]>>> [0, 1] * 0[]>>> [0, 1] * -1[]>>> [0, 1] * -2[]由此可以得出,創建 10 個元素全為 0 的 list 可以這麼做:[0] * 10,我們來看看是不是徹底實現了複製。
>>> a = [0] * 10>>> a[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]>>> a[0] = 1>>> a[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]果然實現了複製,修改第一個元素的值對其餘的元素沒有影響。然而,事情可能沒有那麼簡單,我們再來看一個例子。
>>> a = [[]] * 10>>> a[[], [], [], [], [], [], [], [], [], []]>>> a[0].append(0)>>> a[[0], [0], [0], [0], [0], [0], [0], [0], [0], [0]]我們可以發現,結果並不是我們想要的,一改全改。因此,我們發現乘法有些時候不能進行複製。與此同時,我們希望大 list 裡面的小 list 也實現複製,而不是指向同一個,怎麼做?很簡單,使用列表推導式就可以了,還是這個例子。
>>> a = [[] for _ in range(10)]>>> a[[], [], [], [], [], [], [], [], [], []]>>> a[0].append(0)>>> a[[0], [], [], [], [], [], [], [], [], []]這下應該沒有問題了,針對 list 的元素複製到此結束。如果進行元素複製怕遇到這樣的麻煩就全部使用列表推導式。但是,乘法也要會,因為有些模塊或者是項目的源碼可能會使用乘法!
講完了元素複製,我們來看一下整體複製。因為整體複製針對 list 和 dict 都可以使用,所以我分成兩部分進行講解,先看 list 的整體複製,再看 dict 的整體複製。
我們假設有一個 list 的實例 a,要把它複製給 b,我們能不能直接寫 b = a 呢?其實是不行的,不信的話可以看一下下面的示例。
>>> a = [1, 2, 1]>>> b = a>>> b[2] = 3>>> a[1, 2, 3]我們可以發現,修改 b 的時候,a 也跟著改了,因此我們可以看出 a 和 b 是同一個 list,如何讓 a 和 b 只是值相等,但它們是不同的 list 呢?
最容易想到的方法就是使用 list 的方法 copy,還是這個例子。
>>> a = [1, 2, 1]>>> b = a.copy()>>> b[2] = 3>>> a[1, 2, 1]我們可以發現修改 b 對 a 沒有任何影響,因此完成了複製。
完成複製的操作其核心代碼就是第二行,我們還可以使用下面幾種方法來完成複製。
>>> b = a[:]>>> b = list(a)>>> b = [_ for _ in a]>>> b = a + [] >>> b = a * 1其中後面兩種可能不是那麼容易能夠想得到的,這些方法都能完成複製,大家可以自己進行驗證,我就不做演示了。
但是,這幾種複製的方法都存在一個問題,我拿使用 copy 方法進行複製來演示一下這個問題。
>>> a = [[], []]>>> b = a.copy()>>> b[0].append(0)>>> a[[0], []]我們可以發現複製並沒有那麼徹底,我們把這種不徹底的複製稱之為淺複製。那麼,如何不讓它進行所謂的淺複製?我們後面再說,先繼續看 dict 的整體複製。
我們假設有一個 dict 的實例 a,要把它複製給 b,我們能不能直接寫 b = a 呢?其實是不行的,不信的話可以看一下下面的示例。
>>> a = {'1': 1, '2': 2}>>> b = a>>> b['1'] = 0>>> a{'1': 0, '2': 2}我們可以發現,當 b 修改對應鍵的值時,a 也跟著修改,因此我們可以看出 a 和 b 是同一個 dict,如何讓 a 和 b 只是值相等,但它們是不同的 dict 呢?
最容易想到的方法就是使用 dict 的方法 copy,還是這個例子。
>>> a = {'1': 1, '2': 2}>>> b = a.copy()>>> b['1'] = 0>>> a{'1': 1, '2': 2}我們可以發現修改 b 對 a 沒有任何影響,因此完成了複製。
完成複製的操作其核心代碼就是第二行,我們還可以使用下面幾種方法來完成複製。
>>> b = dict(a)>>> b = {k: a[k] for k in a}但是,這幾種複製的方法都存在一個問題,我拿使用 copy 方法進行複製來演示一下這個問題。
>>> a = {'1': [1], '2': [2]}>>> b = a.copy()>>> b['1'].append(1)>>> a{'1': [1, 1], '2': [2]}我們可以發現這樣的複製都是淺複製,如何不進行淺複製呢?答案很簡單,進行深複製即可。
我首先來講一下什麼是深複製。深複製,即同時複製值及其包含的所有值,等等。大家看了這麼專業的解釋可能會覺得雲裡霧裡,不過不用擔心,我可以讓大家徹底理解深複製,只要記住一句話就行:「深複製是徹底的複製!」
深複製我們沒有必要自己實現,Python 有模塊已經給你實現好了,開箱即用就完事了。還是使用上面的例子進行演示。
>>> a = {'1': [1], '2': [2]}>>> from copy import deepcopy>>> b = deepcopy(a)>>> b['1'].append(1)>>> a{'1': [1], '2': [2]}同樣的,對於 list 套 list 的情況也必須使用深複製。
當然,我從今年開始已經入駐 B 站了!下面給出 B 站帳號:新時代的運籌帷幄,喜歡的可以關注一下,看完視頻不要忘記一鍵三連啊!
今天的文章有不懂的可以後臺回復「加群」,備註:Python 機器學習算法說書人,不備註可是會被拒絕的哦~!