python长度为n的list_python “list*n”的坑你得注意
很多時候我們會用‘listn’的方式來快速得到一個重復元素的新list,比如:
[2]5 = [2, 2, 2, 2, 2]
[0,1]10 = [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
[[0],1]10 = [[0], 1, [0], 1, [0], 1, [0], 1, [0], 1]
……
顯然‘list×n’是將原list里面的所有元素整體復制n次后形成一個新的list。
但是特別注意:如果原list內部元素為“可變對象”(列表、字典、集合),則不會真正復制n份,而是傳遞n個引用給新的list,他們都指向原來的對象,當原list內部元素的元素改變時,新list內部對應元素也跟著變化(同樣新list內部元素的元素變化時,原list也變化);如果原list內部元素為“不可變對象”(數字,字符串,元祖),則不存在這個問題。
下面用詳細例子說明兩者的區別:
1)list內部為可變對象
請看下面例子:
列表a的元素是一個列表(可變對象),b是通過a復制得到的新列表。
a = [['1']]
b = a * 3
print('a={}'.format(a))
print('b={}'.format(b))
# 輸出:
a=[['1']]
b=[['1'], ['1'], ['1']]
下圖展示了a和b之間的關系,可見b并沒有獨立復制一個對象,而和a指向同一個對象。
新建 Microsoft Visio 繪圖.jpg
在前述代碼的基礎上通過a和b來修改內部列表的元素(注意是內部列表的元素),結果發現無論哪種情況,a和b都會同時變化:
a[0].extend([0]) #case 1
#a[0].append(0) #case 2
#a[0][0] = '2' #case 3
#b[0].extend([0]) #case 4
#b[0].append(0) #case 5
#b[0][0] = '2' #case 6
print('a={}'.format(a))
print('b={}'.format(b))
# 輸出:
a=[['1', 0]]
b=[['1', 0], ['1', 0], ['1', 0]]
或
a=[['2']]
b=[['2'], ['2'], ['2']]
上述六種情況分兩大類,其內部存儲結構如下圖,清晰解釋了為什么a和b同時變化了。從圖中可以看出,改變內部list(也就是圖中的“中間list”)的元素,實際加上就是改變了最底層元素,而a和b是共享的,因此會同時變化。
新建 Microsoft Visio 繪圖2.jpg
繼續往下看,下例中只通過改變最外層list的元素,而不對內部list的元素操作,從結果可以看出,無論是單獨改變a還是b,兩者都不會相互影響,b內部元素之間也沒有相互影響:
#case1
a[0] = ['4']
print('a={}'.format(a))
print('b={}'.format(b))
# 輸出:
a=[['4']]
b=[['1'], ['1'], ['1']]
#case2
b[0] = ['4']
print('a={}'.format(a))
print('b={}'.format(b))
# 輸出:
a=[['1']]
b=[['4'], ['1'], ['1']]
下圖展示了為什么會出現這種結果,a[0] = ['4']實際就是將a[0]指向了一個新的對象,而b中所有元素仍然指向原來的中間list,因此a變了,b沒有變。同理b[0] = ['4']也只是將b[0]指向了一個新的對象,而b中的其他元素和a都還指向原來的對象,因此也不會變。
新建 Microsoft Visio 繪圖3.jpg
前述例子中最外層list的的內部元素是list,如果內部元素換成字典或集合,結果也一樣,比如字典(其內部存儲機制與前面完全相同):
a = [{'age':10}]
b = a*3
print('a={}'.format(a))
print('b={}'.format(b))
# 輸出,這沒問題
a=[{'age': 10}]
b=[{'age': 10}, {'age': 10}, {'age': 10}]
#通過a和b來改變他們內部元素的值,即改變字典的值
a[0]['age'] = 20
#b[0]['age'] = 20
print('a={}'.format(a))
print('b={}'.format(b))
# 輸出,可見,a和b同時變了
a=[{'age': 20}]
b=[{'age': 20}, {'age': 20}, {'age': 20}]
#通過將a和b元素指向新的元素,
a[0] = {'name':'Lili'}
print('a={}'.format(a))
print('b={}'.format(b))
# 輸出,可見,只有a變了,
a=[{'name': 'Lili'}]
b=[{'age': 20}, {'age': 20}, {'age': 20}]
或
a=[{'age': 20}]
b=[{'name': 'Lili'}, {'age': 20}, {'age': 20}]
2)list內部為不可變對象
前述情況中,list內部的元素都是可變對象(列表,字典、集合),例如[[],[],[]]或[{},{},{}]
如果內部元素是不可變對象(字符串,數字,元組),例如['a','b']或[1,3],那問題就簡單了,完成不存在前述情況,內部存儲結構也很簡單。
請看下例:
a = ['cgx']
b = a*3
print('a={}'.format(a))
print('b={}'.format(b))
# 輸出,這沒問題
a=['cgx']
b=['cgx', 'cgx', 'cgx']
上述過程的存儲過程如下:
新建 Microsoft Visio 繪圖.jpg
我們企圖通過下面方式來改變內部元素是行不通的,可以通過a[0][0]或b[0][0]來得元素“c”,但企圖通過 a[0][0] = 'C'和b[0][0] = 'C'來將原來的‘c’變為“C”,這是行不通的,因為字符串屬于不可變對象,python禁止這種操作。
a[0][0] = 'C'
b[0][0] = 'C'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in
4 print('b={}'.format(b))
5
----> 6 a[0][0] = 'C'
7 b[0][0] = 'C'
8 # print('a={}'.format(a))
TypeError: 'str' object does not support item assignment
如果只是改變最外層元素(或者說將最外層list元素指向新的對象),那是沒什么問題的,這跟前面可變對象的情況沒什么區別,比如:
a[0] = 'Li'
#b[0] = 'Li'
print('a={}'.format(a))
print('b={}'.format(b))
#輸出
a=['Li']
b=['cgx', 'cgx', 'cgx']
或
a=['cgx']
b=['Li', 'cgx', 'cgx']
可見,通過a[0] = 'Li'或b[0] = 'Li'的操作,僅僅只是改變了a和b最外層單個元素的指向,不會影響其他元素。該過程如下圖所示:
新建 Microsoft Visio 繪圖.jpg
當list的內部元素為“數字”或“元組”時,情況與上述‘字符串’完全相同,此處不再贅述。
總結:
old_list×n = new_list 得到新的list,應該特別注意:
(1) old_list內部的元素是“可變對象”(列表、字典、集合)時,無論是從old_list,還是從new_list 中改變“元素”或“健:值”(通常是利用old_list[][]=或new_list[][]=這種形式操作),都會對所有的對象產生影響;而如果只是對最外層list的對象進行操作(包括替換,重新賦值等,通常是old_list[]=或new_list[]形式),則只影響被操作的對象,不會影響所有對象。
old_list = [[元素,],[元素,],……]
old_list = [{健:值},{健:值},……]
……
(2) old_list內部的元素是“不可變對象”(字符串、數字、元組)時,不存在改變內部元素值的問題(比如下面將‘ab’中的a變為A,這是python不允許的),也就是說old_list[][]=或new_list[][]=這種操作形式不存在。但對最外層list的對象進行操作(包括替換,重新賦值等,通常是old_list[]=或new_list[])與(1)的情況完全相同??梢妼τ诓豢勺儗ο蠖?#xff0c;情況要簡單。
old_list = ['ab','c',……]
old_list = [1,22,……]
old_list = [(1,2),(3,8),……]
……
總結
以上是生活随笔為你收集整理的python长度为n的list_python “list*n”的坑你得注意的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 速读训练软件_记忆力训练:如何提高注意力
- 下一篇: python 时序数据库_时序数据库In