第一部分轉載自:https://www.cnblogs.com/xueli/p/4952063.html
在python中,物件賦值實際上是物件的參照。當建立一個物件,然後把它賦給另一個變數的時候,python並沒有拷貝這個物件,而只是拷貝了這個物件的參照
一般有三種方法,
alist=[
1
,
2
,
3
,[
"a"
,
"b"
]]
(1)直接賦值,預設淺拷貝傳遞物件的參照而已,原始列表改變,被賦值的b也會做相同的改變
>>> b=alist
>>> print b
[1, 2, 3, ['a', 'b']]
>>> alist.append(5)
>>> print alist;print b
[1, 2, 3, ['a', 'b'], 5]
[1, 2, 3, ['a', 'b'], 5]
(2)copy淺拷貝,沒有拷貝子物件,所以原始數據改變,子物件會改變
>>> import copy
>>> c=copy.copy(alist)
>>> print alist;print c
[1, 2, 3, ['a', 'b']]
[1, 2, 3, ['a', 'b']]
>>> alist.append(5)
>>> print alist;print c
[1, 2, 3, ['a', 'b'], 5]
[1, 2, 3, ['a', 'b']]
>>> alist[3]
['a', 'b']
>>> alist[3].append('cccc')
>>> print alist;print c
[1, 2, 3, ['a', 'b', 'cccc'], 5]
[1, 2, 3, ['a', 'b', 'cccc']] 裏面的子物件被改變了
(3)深拷貝,包含物件裏面的自物件的拷貝,所以原始物件的改變不會造成深拷貝裡任何子元素的改變
>>> import copy
>>> d=copy.deepcopy(alist)
>>> print alist;print d
[1, 2, 3, ['a', 'b']]
[1, 2, 3, ['a', 'b']]始終沒有改變
>>> alist.append(5)
>>> print alist;print d
[1, 2, 3, ['a', 'b'], 5]
[1, 2, 3, ['a', 'b']]始終沒有改變
>>> alist[3]
['a', 'b']
>>> alist[3].append("ccccc")
>>> print alist;print d
[1, 2, 3, ['a', 'b', 'ccccc'], 5]
[1, 2, 3, ['a', 'b']] 始終沒有改變
不像matlab一樣,比如b=a,就是單純用a給b賦值,之後a怎麼變,b不會變化。c語言也是一樣。
如果想a怎麼變,b就怎麼變,c語言裡就提出了參照的概念,相當於別名了。
example:
int a; int &ra=a; //定義參照ra,它是變數a的參照,即別名
okay!這篇文章有詳解淺拷貝與深拷貝的本質原因:重新開闢記憶體來儲存
https://www.jianshu.com/p/9ed9b5ce7bb0
https://baijiahao.baidu.com/s?id=1627356407968660842&wfr=spider&for=pc
這篇文章提出pythond的這個設計可以防止數據篡改,或者靈活改變
在Python中,對物件有一種很通俗的說法,萬物皆物件。說的就是構造的任何數據型別都是一個物件,無論是數位、字串、還是函數,甚至是模組、Python都對當做物件處理。
所有Python物件都擁有三個屬性:身份、型別、值。
name="Li J"
print(type(name))
print(id(name))
print(name)
#輸出:
#<type 'str'>
#140334394101408
#Li J
可變與不可變物件
在Python中,按更新物件的方式,可以將物件分爲2大類:可變物件與不可變物件。
可變物件: 列表、字典、集合。所謂可變是指可變物件的值可變,身份是不變的。
不可變物件:數位、字串、元組。不可變物件就是物件的身份和值都不可變。新建立的物件被關聯到原來的變數名,舊物件被丟棄,垃圾回收器會在適當的時機回收這些物件。
var1="python" #字串型別是不可變的
print(id(var1))
var1="java"
print(id(var1))
a=[3,4] #list是可變的,
print(id(a))
a.append(3)
print(id(a))
#輸出結果:
140591145210096
140591145211632
140590909362688
140590909362688
參照
在Python程式中,每個物件都會在記憶體中申請開闢一塊空間來儲存該物件,該物件在記憶體中所在位置的地址被稱爲參照。在開發程式時,所定義的變數名實際就物件的地址參照。
參照實際就是記憶體中的一個數字地址編號,在使用物件時,只要知道這個物件的地址,就可以操作這個物件,但是因爲這個數位地址不方便在開發時使用和記憶,所以使用變數名的形式來代替物件的數位地址。在Python中,變數就是地址的一種表示形式,並不開闢開闢儲存空間。
就像 IP 地址,在存取網站時,實際都是通過 IP 地址來確定主機,而 IP 地址不方便記憶,所以使用域名來代替 IP 地址,在使用域名存取網站時,域名被解析成 IP 地址來使用。
通過一個例子來說明變數和變數指向的參照就是一個東西:
b=18
print(id(b))
print(id(18))
輸出:
29413312
29413312
淺拷貝:
print("淺拷貝:")
import copy
b=[1,2,3,4,5]
print("id b:",id(b))
h=copy.copy(b)
print("id h",id(h))
print(h)
h.append(6)
print(h)
print("id h",id(h))
print(b) #淺拷貝新的列表h改變了,原來的b沒變。
b[1]='n' #列表元素改變後,新的列表也沒變
print(h)
輸出:
淺拷貝:
('id b:', 140165805110552)
('id h', 140165805110480)
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
('id h', 140165805110480)
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
a = [1, 2]
l1 = [3, 4, a]
l2 = copy.copy(l1)
print(l1)
print(l2)
print(id(l1))
print(id(l2))
a[0] = 11
print(id(l1))
print(id(l2))
print(l1)
print(l2)
輸出:
[3, 4, [1, 2]]
[3, 4, [1, 2]]
140624327425704
140624326197400
140624327425704
140624326197400
[3, 4, [11, 2]]
[3, 4, [11, 2]]
可以看出淺拷貝,相當於只拷貝了一層,到a那裏,a變化了,其值也就變化了。
Python中有多種方式實現淺拷貝,copy模組的copy函數、物件的copy函數、工廠方法、切片等;大多數情況下,編寫程式時都是使用淺拷貝,除非有特定的需求;淺拷貝的優點:拷貝速度快,佔用空間少,拷貝效率高。
深拷貝
區別於淺拷貝只拷貝頂層參照,深拷貝會逐層進行拷貝,直到拷貝的所有參照都是不可變參照爲止。
a = [1, 2]
l1 = [3, 4, a]
l2 = copy.deepcopy(l1)
print(l1)
print(l2)
print(id(l1))
print(id(l2))
a[0] = 11
print(id(l1))
print(id(l2))
print(l1)
print(l2)
輸出:
[3, 4, [1, 2]]
[3, 4, [1, 2]]
140673014398488
140672779715720
140673014398488
140672779715720
[3, 4, [11, 2]]
[3, 4, [1, 2]]
爲什麼Python預設的拷貝方式是淺拷貝?
時間角度:淺拷貝花費時間更少;
空間角度:淺拷貝花費記憶體更少;
效率角度:淺拷貝只拷貝頂層數據,一般情況下比深拷貝效率高。
參考文獻
https://www.jianshu.com/p/9ed9b5ce7bb0
https://baijiahao.baidu.com/s?id=1627356407968660842&wfr=spider&for=pc