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