先安利一個網站,對學習編程很有幫助:
http://www.pythontutor.com/
可以逐行可視化執行代碼,具體自行體驗啦
這個網站也是我在看別人的博文時候找到的,也先貼上別人的理解吧,我覺得寫的都很好:
REF:
俗話說得好,師傅領進門,修行靠個人.學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號元素還是相同的引用.
未完待續......
