python长度为n的list_python “list*n”的坑你得注意
很多時(shí)候我們會(huì)用‘listn’的方式來快速得到一個(gè)重復(fù)元素的新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里面的所有元素整體復(fù)制n次后形成一個(gè)新的list。
但是特別注意:如果原list內(nèi)部元素為“可變對(duì)象”(列表、字典、集合),則不會(huì)真正復(fù)制n份,而是傳遞n個(gè)引用給新的list,他們都指向原來的對(duì)象,當(dāng)原list內(nèi)部元素的元素改變時(shí),新list內(nèi)部對(duì)應(yīng)元素也跟著變化(同樣新list內(nèi)部元素的元素變化時(shí),原list也變化);如果原list內(nèi)部元素為“不可變對(duì)象”(數(shù)字,字符串,元祖),則不存在這個(gè)問題。
下面用詳細(xì)例子說明兩者的區(qū)別:
1)list內(nèi)部為可變對(duì)象
請(qǐng)看下面例子:
列表a的元素是一個(gè)列表(可變對(duì)象),b是通過a復(fù)制得到的新列表。
a = [['1']]
b = a * 3
print('a={}'.format(a))
print('b={}'.format(b))
# 輸出:
a=[['1']]
b=[['1'], ['1'], ['1']]
下圖展示了a和b之間的關(guān)系,可見b并沒有獨(dú)立復(fù)制一個(gè)對(duì)象,而和a指向同一個(gè)對(duì)象。
新建 Microsoft Visio 繪圖.jpg
在前述代碼的基礎(chǔ)上通過a和b來修改內(nèi)部列表的元素(注意是內(nèi)部列表的元素),結(jié)果發(fā)現(xiàn)無論哪種情況,a和b都會(huì)同時(shí)變化:
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']]
上述六種情況分兩大類,其內(nèi)部存儲(chǔ)結(jié)構(gòu)如下圖,清晰解釋了為什么a和b同時(shí)變化了。從圖中可以看出,改變內(nèi)部list(也就是圖中的“中間list”)的元素,實(shí)際加上就是改變了最底層元素,而a和b是共享的,因此會(huì)同時(shí)變化。
新建 Microsoft Visio 繪圖2.jpg
繼續(xù)往下看,下例中只通過改變最外層list的元素,而不對(duì)內(nèi)部list的元素操作,從結(jié)果可以看出,無論是單獨(dú)改變a還是b,兩者都不會(huì)相互影響,b內(nèi)部元素之間也沒有相互影響:
#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']]
下圖展示了為什么會(huì)出現(xiàn)這種結(jié)果,a[0] = ['4']實(shí)際就是將a[0]指向了一個(gè)新的對(duì)象,而b中所有元素仍然指向原來的中間list,因此a變了,b沒有變。同理b[0] = ['4']也只是將b[0]指向了一個(gè)新的對(duì)象,而b中的其他元素和a都還指向原來的對(duì)象,因此也不會(huì)變。
新建 Microsoft Visio 繪圖3.jpg
前述例子中最外層list的的內(nèi)部元素是list,如果內(nèi)部元素?fù)Q成字典或集合,結(jié)果也一樣,比如字典(其內(nèi)部存儲(chǔ)機(jī)制與前面完全相同):
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來改變他們內(nèi)部元素的值,即改變字典的值
a[0]['age'] = 20
#b[0]['age'] = 20
print('a={}'.format(a))
print('b={}'.format(b))
# 輸出,可見,a和b同時(shí)變了
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內(nèi)部為不可變對(duì)象
前述情況中,list內(nèi)部的元素都是可變對(duì)象(列表,字典、集合),例如[[],[],[]]或[{},{},{}]
如果內(nèi)部元素是不可變對(duì)象(字符串,數(shù)字,元組),例如['a','b']或[1,3],那問題就簡單了,完成不存在前述情況,內(nèi)部存儲(chǔ)結(jié)構(gòu)也很簡單。
請(qǐng)看下例:
a = ['cgx']
b = a*3
print('a={}'.format(a))
print('b={}'.format(b))
# 輸出,這沒問題
a=['cgx']
b=['cgx', 'cgx', 'cgx']
上述過程的存儲(chǔ)過程如下:
新建 Microsoft Visio 繪圖.jpg
我們企圖通過下面方式來改變內(nèi)部元素是行不通的,可以通過a[0][0]或b[0][0]來得元素“c”,但企圖通過 a[0][0] = 'C'和b[0][0] = 'C'來將原來的‘c’變?yōu)椤癈”,這是行不通的,因?yàn)樽址畬儆诓豢勺儗?duì)象,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元素指向新的對(duì)象),那是沒什么問題的,這跟前面可變對(duì)象的情況沒什么區(qū)別,比如:
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最外層單個(gè)元素的指向,不會(huì)影響其他元素。該過程如下圖所示:
新建 Microsoft Visio 繪圖.jpg
當(dāng)list的內(nèi)部元素為“數(shù)字”或“元組”時(shí),情況與上述‘字符串’完全相同,此處不再贅述。
總結(jié):
old_list×n = new_list 得到新的list,應(yīng)該特別注意:
(1) old_list內(nèi)部的元素是“可變對(duì)象”(列表、字典、集合)時(shí),無論是從old_list,還是從new_list 中改變“元素”或“健:值”(通常是利用old_list[][]=或new_list[][]=這種形式操作),都會(huì)對(duì)所有的對(duì)象產(chǎn)生影響;而如果只是對(duì)最外層list的對(duì)象進(jìn)行操作(包括替換,重新賦值等,通常是old_list[]=或new_list[]形式),則只影響被操作的對(duì)象,不會(huì)影響所有對(duì)象。
old_list = [[元素,],[元素,],……]
old_list = [{健:值},{健:值},……]
……
(2) old_list內(nèi)部的元素是“不可變對(duì)象”(字符串、數(shù)字、元組)時(shí),不存在改變內(nèi)部元素值的問題(比如下面將‘a(chǎn)b’中的a變?yōu)锳,這是python不允許的),也就是說old_list[][]=或new_list[][]=這種操作形式不存在。但對(duì)最外層list的對(duì)象進(jìn)行操作(包括替換,重新賦值等,通常是old_list[]=或new_list[])與(1)的情況完全相同。可見對(duì)于不可變對(duì)象而言,情況要簡單。
old_list = ['ab','c',……]
old_list = [1,22,……]
old_list = [(1,2),(3,8),……]
……
總結(jié)
以上是生活随笔為你收集整理的python长度为n的list_python “list*n”的坑你得注意的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 速读训练软件_记忆力训练:如何提高注意力
- 下一篇: python 时序数据库_时序数据库In