標籤:

Python3.6中sequence的 +=與 extend()有何區別?後者可以在tuple中使用?

本人Python萌新一枚,在看到 Fluent Python中的一個例子,有點難以理解,忘各位大大們幫小弟看看.

如圖所示:

# 在線IDE :Visualize Python, Java, JavaScript, TypeScript, and Ruby code execution

# code segment 1:

# code segment 2:

而且自己在Python的解釋器重新試了一遍 確實如此.

如果說tuple是immutable類型,只能使用 another_t=t.slice(n,m)的方式重新修改.那麼,代碼2的應該符合預期的展示,會報錯.但是為何將 += 方法改為extend()卻不會報錯?

而且 我從文檔中查到:

也就是說 extend()方法和 __iadd__() 這種dunder方法是一致的.既然如此 為何後者會報錯,而前者卻不會報錯?


官方文檔也是個好東西,比如這個問題其實就在FAQ里

Programming FAQ - Python 3.6.2 documentation

——————————————————

其實原因很簡單,兩個操作並不一樣。語義都是不同的。list和tuple比較特殊,其它object或許更容易看出來,但是如果你需要弄明白list和tuple這裡的情況,其實需要看解釋器源碼。

對於第一個寫法,使用的a[2].extend(),對解釋器而言發生了如下事情:

1. 對a這個tuple的getitem

2.調用了extend方法

都沒有衝突所以正常進行。

對於+=,則有些不一樣:

1. 對a進行getitem(2)

2. 對結果調用InplaceConcat,進而調用PyListObject的list_concat並將結果通過a的setitem放回去

而這裡有兩個問題,直接報錯的是setitem,但更深層的問題是,list_concat會新建一個list……儘管由於計數GC的存在,他還會是同一個id,但實際上已經不是同一個對象了(其實list是特例)。所以這裡才存在一個賦值操作,所以才會有setitem,才會報錯。


這個的確是有個陷阱在裡面的,變數的+=都知道是怎麼回事,問題在於索引表達式的+=實際等於三步:

a[i] += b

等效於

t = a[i]

t += b

a[i] = t

而extend只相當於前兩步


tuple中的元素是不允許重新賦值,但是這些元素所引用的對象,則是可以原地修改的(前提對象類型是可修改的)。

調用 extend 是原地修改,修改的是tuple元素所引用的對象。

而 += 則是重新賦值,要直接修改 tuple 元素,當然就要報錯了。

關於 __iadd__:x += y 相當於 x = x.__iadd__(y),而不是直接的 x.__iadd__(y)。可能是要保持賦值語義吧。

Overriding amp;quot;+=amp;quot; in Python? (__iadd__() method)

3. Data model - Python 3.6.2 documentation


推薦閱讀:

Python 常用的標準庫以及第三方庫有哪些?
python有哪些數據分析和數據展現的模塊可以用?
下載了rqalpha源代碼,不知道如何用ipython直接在源代碼中調試運行。?
如何制定python學習計劃?
如何閱讀goagent的代碼?

TAG:Python | Python3x |