詳解Python中表達式i += x與i = i + x是否等價

高洛峰
發布: 2017-02-10 09:11:35
原創
1365 人瀏覽過

這篇文章主要介紹了關於Python中表達式i += x與i = i + x是否等價的相關資料,文中透過範例程式碼介紹的很詳細,相信對大家具有一定的參考價值,有需要的朋友們下面來一起看看吧。

前言

最近看到一個題目,看似很簡單,其實裡面有很深的意義,題目是Python 表達式 i += x 與 i = i + x 等價嗎?如果你的答案是yes,那麼恭喜你正確了50%,為什麼說只對了一半呢? 照我們的一般理解它們兩個是等價的,整數操作時兩者沒什麼異同,但是對於列表操作,是不是也一樣呢?

先看下面兩段代碼:

代碼1

>>> l1 = range(3)
>>> l2 = l1
>>> l2 += [3]
>>> l1
[0, 1, 2, 3]
>>> l2
[0, 1, 2, 3]
登入後複製

代碼2

>>> l1 = range(3)
>>> l2 = l1
>>> l2 = l2 + [3]
>>> l1
[0, 1, 2]
>>> l2
[0, 1, 2, 3]
登入後複製

代碼2

>>> a = 1
>>> id(a)
32574568
>>> a += 1
>>> id(a)
32574544
登入後複製

不一樣,說明i += x 與i = i + x 是不等價的,那什麼情況下等價,什麼情況下不等價呢?

弄清楚這個問題之前,首選得明白兩個概念:可變物件與不可變物件。 在 Python 中任何物件都有的三個通用屬性:唯一標識、類型、值。

唯一標識:用於識別物件的在記憶體中唯一性,它在物件建立之後就不會再改變,函數id()可以查看物件的唯一識別

類型:

決定

類型:

決定了該物件支援哪些操作,不同類型的物件支援的操作就不一樣,例如列表可以有length屬性,而整數沒有。同樣地物件的類型一旦確定了就不會再變,函數 type()可以傳回物件的類型資訊。

物件的值與唯一標識不一樣,並不是所有的物件的值都是一成不變的,有些物件的值可以透過某些操作改變,值可以變化的物件稱之為可變物件(mutable),值不能改變的物件稱之為不可變對象(immutable)

不可變對象(immutable)

详解Python中表达式i += x与i = i + x是否等价
對於不可變對象,值永遠是剛開始創建時候的值,對該對像做的任何操作都會導致一個新的物件的創建。

>>> x = range(3)
>>> y = x

>>> id(x)
139726103041232
>>> id(y)
139726103041232

>>> x.append(3)
>>> x
[0, 1, 2, 3]
>>> y
[0, 1, 2, 3]

>>> id(x)
139726103041232
>>> id(y)
139726103041232
登入後複製

整數「1」 是一個不可變對象,最初賦值的時候,a 指向的是整數對象1 ,但對變數a執行+= 操作後, a 指向另外一個整數對象2 ,但對象1 還是在那裡沒有發生任何變化,而變數a 已經指向了一個新的物件2。常見的不可變物件有:int、tuple、set、str。

详解Python中表达式i += x与i = i + x是否等价可變對象(mutable)

可變對象的值可以透過某些操作動態的改變,例如列表對象,可以透過append方法不斷地向列表中添加元素,該列表的值就在不斷的處於變化中,一個可變對象賦值給兩個變數時,他們共享同一個實例對象,指向相同的記憶體位址,對其中任何一個變數操作時,同時也會影響另一個變數。

详解Python中表达式i += x与i = i + x是否等价

>>> hasattr(int, '__iadd__')
False
>>> hasattr(list, '__iadd__')
True
登入後複製

執行append操作後,對象的記憶體位址不會改變,x、y 依然指向的是原來同一個對象,只不過是他的值發生了變化而已。

理解完可變對象與不可變對象後,回到問題本身,+= 與 +的區別在哪裡呢?

  1. += 操作首先會嘗試呼叫物件的__iadd__方法,如果沒有該方法,那麼嘗試呼叫__add__方法

  2. ,先來看看這兩個方法有什麼區別
  3. __add__和__iadd__ 的差異

__add__ 方法接收兩個參數,傳回它們的和,兩個參數的值都不會改變。

__iadd__ 方法同樣接收兩個參數,但它是屬於in-place 操作,就是說它會改變第一個參數的值,因為這需要物件是可變的,所以對於不可變物件沒有__iadd_ _方法。

🎜🎜
>>> l2 += [3] # 代码1:使用__iadd__,l2的值原地修改
登入後複製
🎜🎜🎜顯然,整數物件是沒有__iadd__的,而列表物件提供了__iadd__方法。 🎜🎜🎜
>>> l2 = l2 + [3] # 代码2:调用 __add__,创建了一个新的列表,赋值给了l2
登入後複製
登入後複製
🎜🎜

代码1中的 += 操作调用的是__iadd__方法,他会原地修改l2指向的那个对象本身的值

详解Python中表达式i += x与i = i + x是否等价

>>> l2 = l2 + [3] # 代码2:调用 __add__,创建了一个新的列表,赋值给了l2
登入後複製
登入後複製

而代码2中的 + 操作调用的是 __add__ 方法,该方法会返回一个新的对象,原来的对象保持不变,l1还是指向原来的对象,而l2已经指向一个新的对象。

详解Python中表达式i += x与i = i + x是否等价

以上就是表达式 i += x 与 i = i + x 的区别。因此对于列表进行 += 操作时,会存在潜在的bug,因为l1会因为l2的变化而发生改变,就像函数的参数不宜使用可变对象作为关键字参数一样。

更多详解Python中表达式i += x与i = i + x是否等价相关文章请关注PHP中文网!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板