成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Python中tuple+=賦值的四個問題

Yujiaao / 1181人閱讀

摘要:原文鏈接最近偶爾翻看,遇到有意思的東西就記下來下面的是在上提出的一個關(guān)于的也就是增量賦值的一個問題。再看的過程,前面都一樣,只有這一行這個直接調(diào)用內(nèi)置函數(shù)完成了對原列表的修改,其中并沒有操作,因此可以正常執(zhí)行。

原文鏈接

最近偶爾翻看Fluent Python,遇到有意思的東西就記下來. 下面的是在PyCon2013上提出的一個關(guān)于tuple的Augmented Assignment也就是增量賦值的一個問題。 并且基于此問題, 又引申出3個變種問題.

問題

首先看第一個問題, 如下面的代碼段:

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

會產(chǎn)生什么結(jié)果呢? 給出了四個選項:

t 變成 [1,2, [30,40,50,60]

TypeError is raised with the message "tuple" object does not support item assignment

Neither 1 nor 2

Both 1 and 2

按照之前的理解, tuple里面的元素是不能被修改的,因此會選2. 如果真是這樣的話,這篇筆記就沒必要了,F(xiàn)luent Python中也就不會拿出一節(jié)來講了。 正確答案是4

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

問題來了,為什么異常都出來了, t還是變了?
再看第二種情況,稍微變化一下,將+=變?yōu)?b>=:

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

結(jié)果就成醬紫了:

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

再看第三種情況,只把+=換為extend或者append,:

>>> t = (1, 2, [30,40])
>>> t[2].extend([50,60])
>>> t
(1, 2, [30, 40, 50, 60])
>>> t[2].append(70)
>>> t
(1, 2, [30, 40, 50, 60, 70])

又正常了,沒拋出異常?

最后第四種情況, 用變量的形式:

>>> a = [30,40]
>>> t = (1, 2, a)
>>> a+=[50,60]
>>> a
[30, 40, 50, 60]
>>> t
(1, 2, [30, 40, 50, 60])
>>> t[2] += [70,80]
Traceback (most recent call last):
  File "", line 1, in 
TypeError: "tuple" object does not support item assignment
>>> t
(1, 2, [30, 40, 50, 60, 70, 80])

又是一種情況, 下面就探究一下其中的原因.

原因

首先需要重溫+=這個運算符,如a+=b:

對于可變對象(mutable object)如list, +=操作的結(jié)果會直接在a對應(yīng)的變量進行修改,而a對應(yīng)的地址不變.

對于不可變對象(imutable object)如tuple, +=則是等價于a = a+b 會產(chǎn)生新的變量,然后綁定到a上而已.

如下代碼段, 可以看出來:

>>> a = [1,2,3]
>>> id(a)
53430752
>>> a+=[4,5]
>>> a
[1, 2, 3, 4, 5]
>>> id(a)
53430752 # 地址沒有變化
>>> b = (1,2,3)
>>> id(b)
49134888
>>> b += (4,5)
>>> b
(1, 2, 3, 4, 5)
>>> id(b)
48560912 # 地址變化了

此外還需要注意的是, python中的tuple作為不可變對象, 也就是我們平時說的元素不能改變, 實際上從報錯信息TypeError: "tuple" object does not support item assignment來看, 更準確的說法是指其中的元素不支持賦值操作=(assignment).

先看最簡單的第二種情況, 它的結(jié)果是符合我們的預(yù)期, 因為=產(chǎn)生了assign的操作.(在由一個例子到python的名字空間 中指出了賦值操作=就是創(chuàng)建新的變量), 因此s[2]=[50,60]就會拋出異常.

再看第三種情況,包含extend/append的, 結(jié)果tuple中的列表值發(fā)生了變化,但是沒有異常拋出. 這個其實也相對容易理解. 因為我們知道tuple中存儲的其實是元素所對應(yīng)的地址(id), 因此如果沒有賦值操作且tuple中的元素的id不變,即可,而list.extend/append只是修改了列表的元素,而列表本身id并沒有變化,看看下面的例子:

>>> a=(1,2,[30,40])
>>> id(a[2])
140628739513736
>>> a[2].extend([50,60])
>>> a
(1, 2, [30, 40, 50, 60])
>>> id(a[2])
140628739513736

目前解決了第二個和第三個問題, 先梳理一下, 其實就是兩點:

tuple內(nèi)部的元素不支持賦值操作

在第一條的基礎(chǔ)上, 如果元素的id沒有變化, 元素其實是可以改變的.

現(xiàn)在再來看最初的第一個問題: t[2] += [50,60] 按照上面的結(jié)論, 不應(yīng)該拋異常啊,因為在我們看來+= 對于可變對象t[2]來說, 屬于in-place操作,也就是直接修改自身的內(nèi)容, id并不變, 確認下id并沒有變化:

>>> a=(1,2,[30,40])
>>> id(a[2])
140628739587392
>>> a[2]+=[50,60]
Traceback (most recent call last):
  File "", line 1, in 
TypeError: "tuple" object does not support item assignment
>>> a
(1, 2, [30, 40, 50, 60])
>>> id(a[2]) # ID 并沒有發(fā)生改變
140628739587392

跟第三個問題僅僅從t[2].extend改成了t[2]+=, 就拋出異常了,所以問題應(yīng)該是出在+=上了.
下面用dis模塊看看它倆執(zhí)行的步驟:
對下面的代碼塊執(zhí)行dis:

t = (1,2, [30,40])
t[2] += [50,60]
t[2].extend([70, 80])

執(zhí)行python -m dis test.py,結(jié)果如下,下面只保留第2,3行代碼的執(zhí)行過程,以及關(guān)鍵步驟的注釋如下:

  2          21 LOAD_NAME                0 (t)
             24 LOAD_CONST               1 (2)
             27 DUP_TOPX                 2
             30 BINARY_SUBSCR                            
             31 LOAD_CONST               4 (50)
             34 LOAD_CONST               5 (60)
             37 BUILD_LIST               2             
             40 INPLACE_ADD
             41 ROT_THREE
             42 STORE_SUBSCR

  3          43 LOAD_NAME                0 (t)
             46 LOAD_CONST               1 (2)
             49 BINARY_SUBSCR
             50 LOAD_ATTR                1 (extend)
             53 LOAD_CONST               6 (70)
             56 LOAD_CONST               7 (80)
             59 BUILD_LIST               2
             62 CALL_FUNCTION            1
             65 POP_TOP
             66 LOAD_CONST               8 (None)
             69 RETURN_VALUE

解釋一下關(guān)鍵的語句:

30 BINARY_SUBSCR: 表示將t[2]的值放在TOS(Top of Stack),這里是指[30, 40]這個列表

40 INPLACE_ADD: 表示TOS += [50,60] 執(zhí)行這一步是可以成功的,修改了TOS的列表為[30,40,50,60]

42 STORE_SUBSCR: 表示s[2] = TOS 問題就出在這里了,這里產(chǎn)生了一個賦值操作,因此會拋異常!但是上述對列表的修改已經(jīng)完成, 這也就解釋了開篇的第一個問題。

再看extend的過程,前面都一樣,只有這一行:

62 CALL_FUNCTION: 這個直接調(diào)用內(nèi)置extend函數(shù)完成了對原列表的修改,其中并沒有assign操作,因此可以正常執(zhí)行。

現(xiàn)在逐漸清晰了, 換句話說,+=并不是原子操作,相當于下面的兩步:

t[2].extend([50,60])
t[2] = t[2]

第一步可以正確執(zhí)行,但是第二步有了=,肯定會拋異常的。 同樣這也可以解釋在使用+=的時候,為何t[2]id明明沒有變化,但是仍然拋出異常了。

現(xiàn)在用一句話總結(jié)下:

tuple中元素不支持assign操作,但是對于那些是可變對象的元素如列表,字典等,在沒有assign操作的基礎(chǔ)上,比如一些in-place操作,是可以修改內(nèi)容的

可以用第四個問題來簡單驗證一下,使用一個指向[30,40]的名稱a來作為元素的值,然后對ain-place的修改,其中并沒有涉及到對tuple的assign操作,那肯定是正常執(zhí)行的。

總結(jié)

這個問題其實以前也就遇到過,但是沒想過具體的原理,后來翻書的時候又看到了, 于是花了點時間把這一個系列查了部分資料以及結(jié)合自己的理解都整理了出來, 算是飯后茶點吧, 不嚴謹?shù)牡胤綗┱堉赋?

部分參考如下:

python bugs

python faq

stackoverflow

Fluent Python

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/40753.html

相關(guān)文章

  • Python每日小知識(3):list和tuple的使用

    摘要:同樣的用上節(jié)講的函數(shù)獲取元素的個數(shù)記住這是獲取的是列表個數(shù)個數(shù)個數(shù)重要的事說三遍。用索引訪問每個元素的位置,索引是從開始的開始的開始的索引也是有容忍限度的超過了就會原地爆炸報錯如果列表中元素個數(shù)賊多,想獲取后面的元素就要實行曲線救國了。 list和tuple是Python內(nèi)置的有序集合,一個是可變的,一個是不可變滴;這都不是事,主要是理解一下指向不變。 看圖說話: showImg(ht...

    SoapEye 評論0 收藏0
  • Python試水

    摘要:你好,特別注意定義編碼格式的這一行代碼必須放在第一行或者第二行,一般如果第一行是提示位置的代碼,那么定義編碼格式的這一行就必須放在第二行,否則依然會提示錯誤。基本功能是進行成員關(guān)系測試和刪除重復(fù)元素。 一、Hello,Python! 試水 test.py print 你好,Python! #python 2.x python (你好,Python!) #python 3.x 很不幸,第...

    zhjx922 評論0 收藏0
  • Python基礎(chǔ)之(五)語句

    摘要:邏輯運算符假設(shè),運算符描述實例布爾與如果為,返回,否則它返回的計算值。布爾或如果是,它返回,否則它返回的計算值。以為例,說明語句。逗號表示打印在同一行本來,在語句中,字符串后面會接一個符號。 運算符 算術(shù)運算符 前面已經(jīng)講過了四則運算,其中涉及到一些運算符:加減乘除,對應(yīng)的符號分別是:+ - * /,此外,還有求余數(shù)的:%。這些都是算術(shù)運算符。其實,算術(shù)運算符不止這些。根據(jù)中學(xué)數(shù)...

    alaege 評論0 收藏0
  • python入門

    摘要:零預(yù)備知識字符編碼計算機只能處理數(shù)字,所以為文本需要轉(zhuǎn)化為數(shù)字才能被計算機處理,計算機里八個比特作為一個字節(jié),這是數(shù)據(jù)的存儲基礎(chǔ)單位。 零、預(yù)備知識 0.1 字符編碼計算機只能處理數(shù)字,所以為文本需要轉(zhuǎn)化為數(shù)字才能被計算機處理,計算機里八個比特(bit)作為一個字節(jié)(byte),這是數(shù)據(jù)的存儲基礎(chǔ)單位。計算機為了處理文本,有以下三種編碼方式: ASCII碼:只有大小寫英文字母,數(shù)字...

    xeblog 評論0 收藏0
  • Python基礎(chǔ)學(xué)習(xí)之常用六大數(shù)據(jù)類型

    摘要:剛開始學(xué)習(xí)一門編程語言,除了了解運行環(huán)境與語言類型之外,最基本還是從該語言的基本數(shù)據(jù)類型開始學(xué)起。六大常用數(shù)據(jù)類型整數(shù)浮點數(shù)字符串列表元組字典講解這些先說一下中的變量與變量名。支持對整數(shù)和浮點數(shù)直接進行四則混合運算。 剛開始學(xué)習(xí)一門編程語言,除了了解運行環(huán)境與語言類型之外,最基本還是從該語言的基本數(shù)據(jù)類型開始學(xué)起。 Python六大常用數(shù)據(jù)類型: int 整數(shù) floa...

    GT 評論0 收藏0

發(fā)表評論

0條評論

Yujiaao

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<