Python引用拷貝賦值


先安利一個網站,對學習編程很有幫助:
http://www.pythontutor.com/

可以逐行可視化執行代碼,具體自行體驗啦


這個網站也是我在看別人的博文時候找到的,也先貼上別人的理解吧,我覺得寫的都很好:

REF:

Python 對象引用、可變性和垃圾回收

python 深入理解 賦值、引用、拷貝、作用域


俗話說得好,師傅領進門,修行靠個人.學python也沒多久,17年的時候走過一遍語法,應該沒完.當時看的byte-of-python.

我覺得這書還行,輕量化,如果學過一門編程語言上手應該挺快的.最近又開始看視頻學習了,看了幾天,然而並沒有實際寫什么程序出來.

到視頻后面感覺跟不上了,頓時明白自己應該叫停了.那些知識需要慢慢消化,不動手是不行的!

在用python的時候總感覺迷迷糊糊的,這次是解決引用賦值的問題.

上面兩篇文章說的很清楚了,我就悄悄把他們剪切一下,方便自己理解:

 

可變對象和不可變對象

在Python中,對象分為兩種:可變對象和不可變對象,不可變對象包括int,float,long,str,tuple等,可變對象包括list,set,dict等。需要注意的是:這里說的不可變指的是值的不可變。對於不可變類型的變量,如果要更改變量,則會創建一個新值,把變量綁定到新值上,而舊值如果沒有被引用就等待垃圾回收。另外,不可變的類型可以計算hash值,作為字典的key。可變類型數據對對象操作的時候,不需要再在其他地方申請內存,只需要在此對象后面連續申請(+/-)即可,也就是它的內存地址會保持不變,但區域會變長或者變短。

示例:

 1 #immutable
 2 a = 12
 3 print(id(a))
 4 a=1
 5 print(id(a))
 6 st = "thisisastring"
 7 print(id(st))
 8 st = "string"
 9 print(id(st))
10 
11 #mutable
12 li = [12,12,342,4]
13 print(id(li))
14 li.append(12)
15 print(id(li))
16 
17 #a new list
18 li = [12,123,213,123,123]
19 print(id(li))
10943360
10943008
140417189762736
140417373204184
140417198413960
140417198413960
140417198537224

可以看出,當對象不可變時,重新賦值,地址已經改變了,也就是重新綁定"貼"到新的值上去了.

當對象可變時,比如上例中的list,追加(append)一個元素后,地址並沒變,只是在原來的基礎上增加一個元素.

下面那句

li = [12,123,213,123,123]

其實是重新創建了一個list,然后把li指向它,所以地址顯然改變了.

賦值和引用

python中的變量名應該理解為標簽,就是常說的引用.在賦值的時候總是把對象的引用值傳給變量.

a=12
b=a
print(id(a),id(b))
b=13
print(id(a),id(b))
10943360 10943360
10943360 10943392

當對象不可變的時候,給已有變量賦值就會創建新對象,然后傳新引用值給變量.而以往接觸的程序語言總是地址不變,而改變其內的值.

起初b=a,b獲得了a的引用,所以ab的地址相同,而給b賦新值時,重新創建了對象,然后把b貼上去了,所以之后ab的地址不同.

1 mylist= [12,13,1234]
2 anotherlist = mylist
3 anotherlist.append(999)
4 print(mylist,id(mylist),id(anotherlist))
5 del mylist[0]
6 print(anotherlist,mylist)
7 
8 del anotherlist
9 print(mylist,id(mylist))
1 [12, 13, 1234, 999] 140417198414536 140417198414536
2 [13, 1234, 999] [13, 1234, 999]
3 [13, 1234, 999] 140417198414536

當對象可變時,直接把一個list賦值給另一個list,傳給那個新list的其實也是引用.

所以mylist和anotherlist其實兩者指向的是一個對象,

然后給anotherlist追加一個元素,再刪除首元素,由於指向一個對象,所以輸出一樣.

最后刪除anotherlist只是刪除了這個標簽而已,因為那個他們指向的對象還有mylist使用.(恩,這個網站確實有助於理解)

拷貝

那么究竟如何將一個對象像以前學的如c語言那樣賦值是創建的獨立的對(bian)象(liang)呢?

在python中這中操作叫拷貝,拷貝又分兩種,淺復制(copy),深復制(deepcopy).

上面說了,直接賦值得到的是引用,要想拷貝就得使用別的操作,常見的有list的切片,list()方法,copy()方法.

alist = [12,12,12,23,23213]
blist = alist[:]
clist = list(alist)
from copy import copy
dlist = copy(alist)
print(alist==blist==clist)
print(id(alist),id(blist),id(clist))
alist.append(77)
print(alist==blist==clist)
print(id(alist),id(blist),id(clist))

True
140417198414216 140417198609096 140417189752712
False
140417198414216 140417198609096 140417189752712

對象相等但是地址不同,說明確實是創建了新對象,之后給alist追加一個元素,三者不等.


 

==和is

這里插播一下兩者的區別,

x = [1, 2, 3]
y = [1, 2, 3]
print(x == y)
print(x is y)
print(id(x))
print(id(y))

執行結果:
True
False
2194144817928
2194144817288

==比較的是對象是否相等,is 比較的是引用是否相等,x和y的對象都是1,2,3但是他們其實是不同的對象,只是值相同,然后x和y則是指向相等的不同的對象的不同的引用了.


為什么說是淺拷貝呢?

1 alist = [12,12,12,[12,23],23213]
2 blist = alist[:]
3 blist.append(777)
4 blist[3][0]=21
5 alist[3][1]=32
6 print(alist,blist)
[12, 12, 12, [21, 32], 23213] [12, 12, 12, [21, 32], 23213, 777]

從輸出的結果看到,無論是從alist中去更改alist[3]還是blist去更改blist[3],結果alist和blist的3號元素都改變了,這樣的拷貝就叫淺拷貝,它只拷貝了最外面那層.

當然看圖片更容易懂了:

從圖片可以看到其它元素都是重新復制創建了一份,但是3號元素還是只是一個引用,和原來alist中那個引用相同.所以任意一個list去修改3號元素另一方會跟着改變.


 

append和extend

再插播一下這兩者的區別:

1 alist = [12,12,12,[12,23],23213]
2 blist = list(alist)
3 alist.append([12,777])
4 blist.extend([12,777])
5 print(alist)
6 print(blist)
[12, 12, 12, [12, 23], 23213, [12, 777]]
[12, 12, 12, [12, 23], 23213, 12, 777]

看輸出就明白不同啦

附:

Help on method_descriptor:

append(...)
    L.append(object) -> None -- append object to end

Help on method_descriptor:

extend(...)
    L.extend(iterable) -> None -- extend list by appending elements from the iterable

 


 再探深拷貝:

1 alist = [12,12,[12,23],23213]
2 blist = alist[:]
3 from copy import deepcopy
4 clist = deepcopy(alist)
5 print(alist==blist==clist)
6 print(id(alist),id(blist),id(clist))
7 print(id(alist[2]),id(blist[2]),id(clist[2]))
8 alist[2][0]=777
9 print((alist),(blist),(clist))
True
140417189861576 140417189752008 140417189860744
140417198413960 140417198413960 140417198612360
[12, 12, [777, 23], 23213] [12, 12, [777, 23], 23213] [12, 12, [12, 23], 23213]

blist由list()方法淺拷貝得來,clist由alist深拷貝而來,輸出的第三行地址可見對於2號元素(一個子list)的地址,alist,blist相同,而clist和他們不同,是重新創建的.

最后打印的結果也說明淺拷貝只是拷貝了外面那層,對於2號元素還是相同的引用.

 

未完待續...... 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM