自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

學習Python一年,這次終于弄懂了淺拷貝和深拷貝

開發(fā) 前端
Python中對象的賦值其實就是對象的引用。當創(chuàng)建一個對象,把它賦值給另一個變量的時候,Python并沒有拷貝這個對象,只是拷貝了這個對象的引用而已。

話說,網(wǎng)上已經(jīng)有很多關(guān)于 Python 淺拷貝和深拷貝的文章了,不過好多文章看起來還是決定似懂非懂,所以決定用自己的理解來寫出這樣一篇文章。

當別人一提起Python中的復(fù)制操作,你會不會立馬站起來說:“我會”,于是就有了如下操作:

import copy

x = copy.copy(y) # 淺拷貝我會了
x = copy.deepcopy(y) # 深拷貝我來了

那淺拷貝和深拷貝有什么區(qū)別呢,你能給我講講嗎?

1、從引用vs.拷貝說起

首先,我們要弄清楚什么是對象引用與對象拷貝(復(fù)制)。

對象引用

Python中對象的賦值其實就是對象的引用。當創(chuàng)建一個對象,把它賦值給另一個變量的時候,Python并沒有拷貝這個對象,只是拷貝了這個對象的引用而已。

>>> a = 1
>>> b = a
>>> id(a) == id(b)
True
>>> x = [1, 2, 3]
>>> y = [x, 4]
>>> x
[1, 2, 3]
>>> y
[[1, 2, 3], 4]
>>>
>>>> id(x) == id(y)
False
>>> id(x) == id(y[0])
True

如果這個過程不理解,可以看看下圖:

圖片

當我們對 x 列表進行操作時,會發(fā)現(xiàn) y 中也發(fā)生了意料之外的事情:

>>> x[1] = 2020
>>> y
[[1, 2020, 3], 4]

由于列表是可變的,修改x這個列表對象的時候,也會改變對象 y 中對 x 的引用。

所以當我們在原處修改可變對象時 可能會影響程序中其他地方對相同對象的其他引用,這一點很重要。如果你不想這樣做,就需要明確地告訴 Python 復(fù)制該對象。

對象拷貝

如果你需要拷貝,可以進行如下操作:

  • 沒有限制條件的分片表達式(L[:])
  • 工廠函數(shù)(如list/dir/set)
  • 字典copy方法(X.copy())
  • copy標準庫模塊(import copy)

舉個例子,假設(shè)有一個列表L和一個字典D:

>>> L = [2019, 2020, 2021]
>>> D = {'1':2019, '2':2020, '3':2021}
>>>
>>> A = L[:] # 區(qū)分 A=L 或 A = List(L)
>>> B = D.copy() # 區(qū)分 B=D
>>> A
[2019, 2020, 2021]
>>> B
{'1': 2019, '2': 2020, '3': 2021}

圖片

這樣定義之后,當你修改A和B時,會發(fā)現(xiàn)并不會對原來的L跟D產(chǎn)生影響,因為,這就是對象的拷貝。

>>> A[1] = 'happy'
>>> B[3] = 'today'
>>> L, D
([2019, 2020, 2021], {'1': 2019, '2': 2020, '3': 2021})
>>> A, B
([2019, 'happy', 2021], {'1': 2019, '2': 2020, '3': 2021, 3: 'today'})

上述對列表和字典的拷貝操作默認都為淺拷貝:

  • 制作字典的淺層復(fù)制可以使用dict.copy() 方法
  • 而制作列表的淺層復(fù)制可以通過賦值整個列表的切片完成,例如,copied_list = original_list[:]。

說到這里,疑問就產(chǎn)生了?什么是淺拷貝?淺拷貝的對應(yīng)深拷貝又該作何解釋?

2、談?wù)劀\拷貝和深拷貝

官方文檔定義:

淺層復(fù)制和深層復(fù)制之間的區(qū)別僅與復(fù)合對象 (即包含其他對象的對象,如列表或類的實例) 相關(guān):

一個淺層復(fù)制 會構(gòu)造一個新的復(fù)合對象,然后(在可能的范圍內(nèi))將原對象中找到的 引用 插入其中。

一個深層復(fù)制 會構(gòu)造一個新的復(fù)合對象,然后遞歸地將原始對象中所找到的對象的 副本 插入。

淺拷貝

?淺拷貝:拷貝了最外圍的對象本身,內(nèi)部的元素都只是拷貝了一個引用而已。也就是,把對象復(fù)制一遍,但是該對象中引用的其他對象我不復(fù)制。

用通俗的話理解就是:你的櫥柜(對象)里裝著一??(籃子)??(雞蛋),然后淺拷貝一下的意思。我只拷貝了最外面的這個櫥柜,至于里面的內(nèi)部元素(??和??)我并不拷貝。

當我們遇到簡單的對象時,用上面的解釋好像很好理解;如果遇到復(fù)合對象,就比如下列代碼:

l1 = [3, [66, 55, 44], (3, 7, 21)]
l2 = list(l1)
l1.append(100)
print('l1:', l1)
print('l2:', l2)
l1[1].remove(55)
l2[1] += [33, 22]
l2[2] += (9, 9, 81)
print('l1:', l1)
print('l2:', l2)

代碼解釋:

  • l2是l1的淺拷貝
  • 把100追加到l1,對l2沒有影響
  • 1內(nèi)部列表l1[1中的55刪除,對l2也產(chǎn)生影響,因為l1[1]和l2[1]綁定的是同一個列表
  • 對可變對象來說,l2[1引用的列表進行+=就地修改列表。這次修改導致l1[1]也發(fā)生了改變
  • 對元組來說,+= 運算符創(chuàng)建一個新元組,然后重新綁定給變量 l2[2]。這等同于l2[2] = l2[2] + (10, 11)?,F(xiàn)在,l1 和 l2 中最 后位置上的元組不是同一個對象

把這段代碼可視化出來如下:

圖片

動手試一試,可以點此處

深拷貝

?深拷貝:外圍和內(nèi)部元素都進行了拷貝對象本身,而不是引用。也就是,把對象復(fù)制一遍,并且該對象中引用的其他對象我也復(fù)制。

對比上面的籃子和雞蛋:你的櫥柜(對象)里裝著一??(籃子)??(雞蛋),然后深拷貝一下的意思。把最外面的這個櫥柜和里面的內(nèi)部元素(??和??)全部拷貝過來。

圖片

from copy import deepcopy
l1 = [3, [66, 55, 44], (3, 7, 21)]
l2 = deepcopy(l1)
l1.append(100)
print('l1:', l1)
print('l2:', l2)
l1[1].remove(55)
l2[1] += [33, 22]
l2[2] += (9, 9, 81)
print('l1:', l1)
print('l2:', l2)

輸出結(jié)果:

圖片

拷貝的特點

  1. 不可變類型的對象(如數(shù)字、字符串、和其他'原子'類型的對象)對于深淺拷貝毫無影響,最終的地址值和值都是相等的。也就是,"obj is copy.copy(obj)" 、"obj is copy.deepcopy(obj)"
  2. 可變類型的對象=淺拷貝:值相等,地址相等copy淺拷貝:值相等,地址不相等deepcopy深拷貝:值相等,地址不相等
  3. 循環(huán)引用的對象如果對象有循環(huán)引用,那么這個樸素的算法會進入無限循環(huán)。deepcopy 函數(shù)會記住已經(jīng)復(fù)制的對象,因此能優(yōu)雅地處理循環(huán)引用。

循環(huán)引用:b 引用 a,然后追加到 a 中;deepcopy 會想辦法復(fù)制 a,而copy會進入無限循環(huán)。如下面代碼:

from copy import deepcopy, copy
a = [80, 90]
b = [a, 100]
a.append(b)
print("a:", a)
print("b:", b)

c = deepcopy(a)
print("c:", c)

d = copy(b)
print("d:", d)

輸出結(jié)果:

a: [80, 90, [[...], 100]]
b: [[80, 90, [...]], 100]
c: [80, 90, [[...], 100]]
d: [[80, 90, [[...], 100]], 100]

深淺拷貝的作用

1,減少內(nèi)存的使用2,以后在做數(shù)據(jù)的清洗、修改或者入庫的時候,對原數(shù)據(jù)進行復(fù)制一份,以防數(shù)據(jù)修改之后,找不到原數(shù)據(jù)。3. 可以定制復(fù)制行為,通過實現(xiàn)__copy()和__deep__()方法來控制。

3、總結(jié)

看完這篇文章后,轉(zhuǎn)身就跟你同桌說:“x同學,聽說你最近在學Python,你知道淺拷貝和深拷貝嗎?”“不知道,學得有點暈”“沒事,我來給你講講:”

拷貝其實在開始學好幾個操作語句中,我們就已經(jīng)使用過卻可能不知道的(前3個),而且淺拷貝是Python的默認拷貝方式。拷貝的方法如下:

  1. 可變類型的切片操作:[:]
  2. 工廠函數(shù)(如list/dir/set)
  3. 字典copy方法(X.copy())
  4. 然后就是Python有專門的copy標準庫模塊:包含兩個方法copy()和deepcopy()

淺拷貝就像是我只拷貝最外圍的對象,對象中引用的其他對象我不復(fù)制。深拷貝就是完整的把對象和對象里的內(nèi)容都拷貝過來。拷貝的目的:

  1. 為了節(jié)省內(nèi)存
  2. 防止數(shù)據(jù)丟失。

后記:深淺拷貝的坑及難以理解的點也只在復(fù)合對象上,簡單對象就是我們平常理解的復(fù)制。而針對非容器類型(如數(shù)字、字符串、和其他'原子'類型的對象)沒有被拷貝一說。

要是你的同桌還是不懂,你就把這篇文章甩給他,讓他好好看看(偷笑)。如果你覺得這篇文章還不錯,請點個贊或者收個藏,點個關(guān)注更好啦。

責任編輯:武曉燕 來源: 宇宙之一粟
相關(guān)推薦

2021-09-27 11:07:11

深拷貝淺拷貝內(nèi)存

2022-07-26 08:07:03

Python淺拷貝深拷貝

2017-08-16 13:30:05

Java深拷貝淺拷貝

2021-07-16 12:33:24

Javascript深拷貝淺拷貝

2023-09-22 12:21:33

Python深拷貝淺拷貝

2019-02-25 08:58:16

Python深拷貝淺拷貝

2020-06-23 08:41:47

JavaScript開發(fā)技術(shù)

2020-08-03 08:24:26

原型模式拷貝

2018-09-26 14:37:17

JavaScript前端編程語言

2021-01-08 06:15:09

深拷貝淺拷貝寫時拷貝

2023-05-17 08:42:46

深拷貝Golang

2021-09-10 07:41:06

Python拷貝Python基礎(chǔ)

2009-05-19 17:28:44

深拷貝淺拷貝clone()

2024-03-15 15:03:23

2020-10-12 08:35:22

JavaScript

2024-04-17 09:01:08

Python深拷貝淺拷貝

2023-05-17 07:36:00

淺拷貝深拷貝對象

2018-05-10 14:20:18

前端JavaScript深拷貝

2022-09-30 15:03:09

C語言深拷貝淺拷貝

2024-02-05 22:56:16

C++拷貝開發(fā)
點贊
收藏

51CTO技術(shù)棧公眾號