【Python学习】——语言风格(变量赋值、深浅拷贝、for循环陷阱)
目錄
1、賦值
?2、賦值的分類——引用賦值、值賦值
1) 不可變對(duì)象引用賦值——字符串、數(shù)值、元組等
2)可變對(duì)象引用賦值——列表、集合、字典
3)可變與不可變對(duì)象的引用賦值內(nèi)部分析
4)在py文件中,和作用域有關(guān),如在同一個(gè)函數(shù)中的相同值的變量是相等的,即值相等,地址也相等
3、深拷貝與淺拷貝
4、循環(huán)——序列和非序列的循環(huán)中進(jìn)行元素的修改
?
1、賦值
# 賦值包含多種賦值方式,一般賦值、元組賦值、序列賦值、解包賦值 a = "long" b,c = "1",2 d,e,f,g = "long" h,*i = "long" print(a) print(b) print(c) print(d) print(e) print(f) print(g) print(h) print(i)long
1
2
l
o
n
g
l
['o', 'n', 'g']
當(dāng)使用一個(gè)*前綴變量的時(shí)候,表示將序列中對(duì)應(yīng)的元素全部收集到一個(gè)列表中(注意,總是一個(gè)列表),這個(gè)列表名為*開頭的那個(gè)變量名。*號(hào)可以出現(xiàn)在任意位置處,只要賦值的時(shí)候能前后對(duì)應(yīng)位置關(guān)系即可。
注意其中的幾個(gè)關(guān)鍵字:序列、對(duì)應(yīng)的元素、列表
- 序列意味著可以是列表、元組、字符串等等
- 列表意味著只要收集不報(bào)錯(cuò),賦值給解包變量的一定是一個(gè)列表
- 對(duì)應(yīng)的元素意味著可能收集到0或任意個(gè)元素到列表。
不管如何,收集的結(jié)果總是列表,只不過可能是空列表或者只有一個(gè)元素的列表。
?
兩個(gè)注意事項(xiàng):
?2、賦值的分類——引用賦值、值賦值
引用賦值——指的是將內(nèi)存地址賦值給變量來實(shí)現(xiàn)賦值
1) 不可變對(duì)象引用賦值——字符串、數(shù)值、元組等
a = 1000
b = a
a = 2000
前兩行b=a是將1000的地址賦值給b,即a和b都是指向值1000的內(nèi)存地址。第三行a=2000是對(duì)a重新進(jìn)行賦值,因?yàn)閿?shù)值是不可改變的對(duì)象,因此會(huì)先開辟一個(gè)內(nèi)存地址用于存儲(chǔ)2000,然后將a指向2000
不可變對(duì)象變量之間不會(huì)互相影響,即如果一開始兩個(gè)變量指向同一個(gè)內(nèi)存地址,當(dāng)其中一個(gè)變量的值發(fā)生了改變的時(shí)候,另一個(gè)變量不會(huì)受到影響
對(duì)于不可變對(duì)象,修改變量的值意味著在內(nèi)存中要新創(chuàng)建一個(gè)數(shù)據(jù)對(duì)象
a = 10000 b = a a = 20000>>> a,b (20000, 10000)2)可變對(duì)象引用賦值——列表、集合、字典
對(duì)于可變對(duì)象,比如列表,它是在"原處修改"數(shù)據(jù)對(duì)象的(注意加了雙引號(hào))。比如修改列表中的某個(gè)元素,列表的地址不會(huì)變,還是原來的那個(gè)內(nèi)存對(duì)象,所以稱之為"原處修改"。例如:
L1 = [111,222,333] L2 = L1 L1[1] = 2222>>> L1,L2 ([111, 2222, 333], [111, 2222, 333])L2是通過引用賦值得到的值,值為可變對(duì)象列表,當(dāng)對(duì)L1改變一個(gè)列表元素時(shí),其列表的地址不會(huì)發(fā)生改變,因此其L2的值也會(huì)發(fā)生相應(yīng)的改變
在L1[1]賦值的前后,數(shù)據(jù)對(duì)象[111,222,333]的地址一直都沒有改變,但是這個(gè)列表的第二個(gè)元素的值已經(jīng)改變了。因?yàn)長1和L2都指向這個(gè)列表,所以L1修改第二個(gè)元素后,L2的值也相應(yīng)地到影響。也就是說,L1和L2仍然是同一個(gè)列表對(duì)象[111,2222,333]。
結(jié)論是:對(duì)于可變對(duì)象,變量之間是相互影響的。
3)可變與不可變對(duì)象的引用賦值內(nèi)部分析
可變對(duì)象和不可變對(duì)象的賦值形式雖然一樣,但是修改數(shù)據(jù)時(shí)的過程不一樣。
對(duì)于不可變對(duì)象,修改數(shù)據(jù)是直接在堆內(nèi)存中新創(chuàng)建一個(gè)數(shù)據(jù)對(duì)象。如圖:
對(duì)于可變對(duì)象,修改這個(gè)可變對(duì)象中的元素時(shí),這個(gè)可變對(duì)象的地址不會(huì)改變,所以是"原處修改"的。但需要注意的是,這個(gè)被修改的元素可能是不可變對(duì)象,可能是可變對(duì)象,如果被修改的元素是不可變對(duì)象,就會(huì)創(chuàng)建一個(gè)新數(shù)據(jù)對(duì)象,并引用這個(gè)新數(shù)據(jù)對(duì)象,而原始的那個(gè)元素將等待垃圾回收器回收。
>>> L=[333,444,555] >>> id(L),id(L[1]) (56583832, 55771984) >>> L[1]=4444 >>> id(L),id(L[1]) (56583832, 55771952)如圖所示:
4)在py文件中,和作用域有關(guān),如在同一個(gè)函數(shù)中的相同值的變量是相等的,即值相等,地址也相等
3、深拷貝與淺拷貝
1)深拷貝
完全創(chuàng)建一個(gè)新的數(shù)據(jù)對(duì)象,不會(huì)受到其他變量的元素值變化的影響
2)淺拷貝,只是拷貝了第一層的元素,若第一層的元素是可變對(duì)象,則引用的是可變對(duì)象的地址,因此還是會(huì)受到其他變量的影響
# 引用賦值——只是得到了地址 print("賦值----------------------") L = [1,2,[3,4,5]] L2 = L print("修改元素前-----------") print(L) print(L2) print("修改元素后-----------") L[0] = 0 print(L) print(L2)賦值----------------------
修改元素前-----------
[1, 2, [3, 4, 5]]
[1, 2, [3, 4, 5]]
修改元素后-----------
[0, 2, [3, 4, 5]]
[0, 2, [3, 4, 5]]?
?
淺拷貝------------------------
淺拷貝1------------------------
修改不可變對(duì)象的元素前-----------
[1, 2, [3, 4, 5], 6]
[1, 2, [3, 4, 5], 6]
修改不可變對(duì)象的元素后-----------
[0, 2, [3, 4, 5], 6]
[1, 2, [3, 4, 5], 6]
淺拷貝1------------------------
修改可變對(duì)象的元素前-----------
[1, 2, [3, 4, 5], 6]
[1, 2, [3, 4, 5], 6]
修改可變對(duì)象的元素后-----------
[1, 2, [0, 4, 5], 6]
[1, 2, [0, 4, 5], 6]
淺拷貝2------------------------
修改元素前-----------
[1, 2, [3, 4, 5]]
[1, 2, [3, 4, 5]]
修改元素后-----------
[0, 2, [0, 4, 5]]
[1, 2, [0, 4, 5]]
深拷貝------------------------
[0, 2, [0, 4, 5]]
[1, 2, [3, 4, 5]]
一般我們使用到的都是淺拷貝
4、循環(huán)——序列和非序列的循環(huán)中進(jìn)行元素的修改
https://www.cnblogs.com/f-ck-need-u/p/10129317.html
1)列表進(jìn)行原地修改時(shí)(L+=[val1,val2]),進(jìn)行后面的迭代時(shí),進(jìn)行迭代的是修改后的列表,因?yàn)閒or是一個(gè)迭代器,使用的是next,即通過索引進(jìn)行的,因此列表原地修改會(huì)導(dǎo)致元素出現(xiàn)奇怪的現(xiàn)象
為了避免這種情況,我們對(duì)列表進(jìn)行修改時(shí),建議生成一個(gè)新的列表對(duì)象來進(jìn)行存放
L = ['a','b','c','d','e']## 原處修改列表,新元素f、g也會(huì)被迭代 for i in L:if i in "de":L += ["f", "g"]print(i)## 創(chuàng)建新列表,新元素f、g不會(huì)被迭代 for i in L:if i in "de":L = L + ["f", "g"]print(i)這個(gè)for迭代器在迭代剛開始的時(shí)候,先找到L所指向的迭代對(duì)象,即內(nèi)存中的[1,2,3,4]。如果迭代過程中如果L變成了一個(gè)集合,或另一個(gè)列表對(duì)象,for的迭代并不會(huì)收到影響。但如果是在原處修改這個(gè)列表,那么迭代將會(huì)收到影響,例如新增元素也會(huì)被迭代到。
這里通過列表索引來進(jìn)行元素的遍歷和修改即可避免上面的情況
2)迭代一個(gè)列表,迭代過程中刪除一個(gè)列表元素。
L = ['a','b','c','d','e'] for i in L:if i in "bc":L.remove(i)print(i)print(L)輸出的結(jié)果將是:
b ['a', 'c', 'd', 'e']這個(gè)for循環(huán)的本意是想刪除b、c元素,但結(jié)果卻只刪除了b。通過結(jié)果可以發(fā)現(xiàn),c根本就沒有被for迭代。之所以會(huì)這樣,是因?yàn)榈絙的時(shí)候,滿足if條件,然后刪除了列表中的b元素。正因?yàn)閯h除操作,使得列表中b后面的元素整體前移一個(gè)位置,也就是c元素的索引位置變成了index=1,而index=1的元素已經(jīng)被for迭代過(即元素b),使得c幸運(yùn)地逃過了for的迭代。
3)迭代的是字典或者集合時(shí),雖然兩者都是可變序列,但是時(shí)無序的,因此在迭代的過程中,是不允許字典或者集合發(fā)生改變的,否則會(huì)報(bào)錯(cuò)
D = {'a':1,'b':2,'c':3,'d':4,'e':5}for i in D:if i in "bc":del D[i]print(i)print(D)報(bào)錯(cuò):
b Traceback (most recent call last):File "g:/pycode/lists.py", line 12, in <module>for i in D: RuntimeError: dictionary changed size during iteration S = {'a','b','c','d','e'}for i in S:if i in "bc":S.remove(i)print(i)print(S)報(bào)錯(cuò):
b Traceback (most recent call last):File "g:/pycode/lists.py", line 4, in <module>for i in L: RuntimeError: Set changed size during iteration若想修改字典的話,我們可以復(fù)制一份數(shù)據(jù)對(duì)象作為副本,然后將副本進(jìn)行迭代,將原來的字典或集合作為修改對(duì)象?
D = {'a':1,'b':2,'c':3,'d':4,'e':5}for i in D.copy():if i in "bc":D.pop(i)print(i) print(D)S = {'a','b','c','d','e'}for i in S.copy():if i in "bc":S.remove(i)print(i) print(S)?注意:在進(jìn)行可變對(duì)象數(shù)據(jù)對(duì)象的迭代與修改時(shí),我們只需要將迭代對(duì)象和修改對(duì)象分開就不會(huì)出現(xiàn)上述的錯(cuò)誤。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的【Python学习】——语言风格(变量赋值、深浅拷贝、for循环陷阱)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 自制操作系统Antz -- 系列文章
- 下一篇: 腾讯技术研究类和数据分析第一次笔试(20