日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
序列賦值引發(fā)的Python列表陷進(jìn)

本文轉(zhuǎn)載自微信公眾號(hào)「dongfanger」,作者dongfanger。轉(zhuǎn)載本文請(qǐng)聯(lián)系dongfanger公眾號(hào)。

成都創(chuàng)新互聯(lián)公司于2013年成立,先為大埔等服務(wù)建站,大埔等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為大埔企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。

序列賦值是Python默認(rèn)操作,如果使用不當(dāng),有可能會(huì)掉入語法陷阱。

+

+是指把兩個(gè)序列的元素拼接在一起。通常+號(hào)兩側(cè)的序列由相同類型的數(shù)據(jù)所構(gòu)成,在拼接的過程中,兩個(gè)被操作的序列都不會(huì)被修改,Python會(huì)新建一個(gè)包含同樣類型數(shù)據(jù)的序列作為拼接的結(jié)果。比如:

 
 
 
  1. a = [1]
  2. b = [2]
  3. c = a + b
  4. print(a, b, c)
  5. print(id(a), id(b), id(c))

結(jié)果為:

 
 
 
  1. [1] [2] [1, 2]
  2. 2409610524480 2409610523520 2409610523648

*

如果想要把一個(gè)序列復(fù)制幾份然后再拼接起來,更快捷的做法是把這個(gè)序列乘以一個(gè)整數(shù)。同樣,這個(gè)操作會(huì)產(chǎn)生一個(gè)新序列:

 
 
 
  1. >>> l = [1]
  2. >>> l * 5
  3. [1, 1, 1, 1, 1]
  4. >>> 5 * "a"
  5. 'aaaaa'

+和*都遵循這個(gè)規(guī)律,不修改原有的操作對(duì)象,而是構(gòu)建一個(gè)全新的序列。

列表套列表的陷進(jìn)

猜猜這個(gè)結(jié)果會(huì)是啥:

 
 
 
  1. x = ["x"]
  2. my_list = [x] * 3
  3. print(my_list)  # [['x'], ['x'], ['x']]
  4. x2 = my_list[2]
  5. x2[0] = "y"
  6. print(my_list)

講道理,應(yīng)該是[['x'], ['x'], ['y']],但是錯(cuò)了,實(shí)際是:

 
 
 
  1. [['y'], ['y'], ['y']]

Unbelievable!給my_list的最后一個(gè)元素的列表賦值,結(jié)果所有三個(gè)元素的列表都被賦值了!這反映出my_list這三個(gè)元素不是3個(gè)列表,而是3個(gè)列表引用,指向了同一個(gè)相同的列表。相當(dāng)于:

 
 
 
  1. x = ["x"]
  2. my_list = []
  3. for i in range(3):
  4.     my_list.append(x)  # 追加相同對(duì)象
  5. x2 = my_list[2]
  6. x2[0] = "y"
  7. print(my_list)  # [['y'], ['y'], ['y']]

每次都追加了同一個(gè)對(duì)象到my_list。如果想生成3個(gè)不同列表,那么需要在每次迭代中新建列表:

 
 
 
  1. my_list = []
  2. for i in range(3):
  3.     x = ["x"]  # 新建列表
  4.     my_list.append(x)
  5. x2 = my_list[2]
  6. x2[0] = "y"
  7. print(my_list)  # [['x'], ['x'], ['y']]

這樣就符合預(yù)期了??梢杂昧斜硗茖?dǎo)簡化代碼:

 
 
 
  1. x = ["x"]
  2. my_list = [x for i range(3)]
  3. x2 = my_list[2]  
  4. x2[0] = "y"
  5. print(my_list)  # [['x'], ['x'], ['y']]

教訓(xùn):

新建列表中的列表,使用列表推導(dǎo),不要使用*運(yùn)算符。

如果a * n這個(gè)語句中,序列a里的元素是對(duì)其他可變對(duì)象的引用的話,就需要格外注意了,這可能不是你想要的效果。

+=

a += b雖然意思是a = a + b,但是它背后的特殊方法是__iadd__,如果一個(gè)類沒有實(shí)現(xiàn)這個(gè)方法的話,Python才會(huì)退一步調(diào)用__add__。__iadd__方法會(huì)直接在原對(duì)象中追加,__add__方法會(huì)先生成新對(duì)象再賦值。

*=

+=的這些概念也適用于*=,只是后者對(duì)應(yīng)的是__imul__。追加還是新對(duì)象,在作用到可變序列和不可變序列時(shí)效果明顯,示例:

 
 
 
  1. # 可變序列,追加
  2. >>> l = [1, 2, 3]
  3. >>> id(l)
  4. 2135319475136
  5. >>> l *= 2
  6. >>> l
  7. [1, 2, 3, 1, 2, 3]
  8. >>> id(l)
  9. 2135319475136  # id一樣
  10. # 不可變序列,新對(duì)象
  11. >>> t = (1, 2, 3)
  12. >>> id(t)
  13. 2135322139520
  14. >>> t *= 2
  15. >>> id(t)
  16. 2135321695424  # id不一樣

元組套列表的陷進(jìn)

 
 
 
  1. >>> t = (1, 2, [30, 40])
  2. >>> t[2] += [50, 60]

猜猜會(huì)發(fā)生下面4種情況中的哪一種?a.t變成(1, 2, [30, 40, 50, 60])b.因?yàn)閠uple不支持對(duì)它的元素賦值,所以會(huì)拋出TypeError異常c.以上兩個(gè)都不是d.a和b都是對(duì)的因?yàn)樵M不能賦值,所以我會(huì)毫不猶豫的選擇b。但實(shí)際上答案是d!a和b都是對(duì)的,既會(huì)賦值成功,也會(huì)報(bào)錯(cuò):

 
 
 
  1. >>> t = (1, 2, [30, 40])
  2. >>> t[2] += [50, 60]
  3. Traceback (most recent call last):
  4.   File "", line 1, in 
  5. TypeError: 'tuple' object does not support item assignment
  6. >>> t
  7. (1, 2, [30, 40, 50, 60])

Oh No!為什么?一、賦值成功,因?yàn)閠[2]指向的是一個(gè)可變對(duì)象(列表[30, 40]),可變對(duì)象是能賦值的。二、報(bào)錯(cuò),因?yàn)榭勺儗?duì)象賦值給了不可變對(duì)象(元組t),不可變對(duì)象不能賦值。

寫成t[2].extend([50, 60])能避免這個(gè)異常。

教訓(xùn):

  1. 不要把可變對(duì)象放在元組里面。
  2. +=不是一個(gè)原子操作,雖然拋出了異常,但還是完成了操作。

這位巴西作者說到,在他15年的Python生涯中,他還沒見過誰在這個(gè)地方吃過虧。

小結(jié)

本文分別介紹了+、*和列表套列表的陷阱,+=、*=和元組套列表的陷阱,并分別得出了教訓(xùn)。這是動(dòng)態(tài)語言的弊端,在運(yùn)行后才能知道有沒有類型錯(cuò)誤,只能積累代碼經(jīng)驗(yàn)來避免。魚與熊掌不可兼得,在享受Python語法簡潔的便利同時(shí),也得付出運(yùn)行報(bào)錯(cuò)排查麻煩的代價(jià)。

參考資料:《流暢的Python》


本文標(biāo)題:序列賦值引發(fā)的Python列表陷進(jìn)
本文地址:http://m.5511xx.com/article/djgihsj.html