標籤:

python中的賦值,什麼時候是傳值什麼時候是傳址?

s = [1, 2, 3]

t = s

t.reverse()

然後s和t都變成了[3, 2, 1]

但是如果s = [1, 2, 3]

t = s[::-1]

只有t是[3, 2, 1] s還是[1, 2, 3]不變的...

所以我比較奇怪,python中的賦值,什麼時候是傳值什麼時候是傳址?


Python一切皆為對象。賦值一直都是傳址。所有變數都是保存著對象的地址。

首先,分析一下題主所描述的兩種情況出現不同的原因。

第一種情況將s賦值給了t,此時s和t指向了同一個對象。所以執行reverse時,對象本身被改變。因為s和t指向同一個對象,所以你無論輸出s還是t都是輸出同一個已經被reverse的對象。

第二種情況是對s執行了一個slicing的操作。此時本身s[::-1]返回的不是s對象本身,而是一個在內存中根據運算重新生成的對象,所以t得到的是一個s[::-1]生成的新對象的地址。而s還是保留著原來的對象,由於s[::-1]不會改變原來對象的值,所以s的值是不會改變的。

再進一步。

在Python中,即使是整數類型,它也是按照對象來處理的。例如a=1,它並不是將1值賦值給了a,而是將一個整數對象1的地址賦值給了a。由於Python對小整數的特殊處理,凡是在一定範圍內的小整數,是統一使用了「小整數對象池」。也就是說所有的小整數,例如1,都是使用對象池裡面的同一個對象。但是,小整數對象池是有限的,範圍是[-5, 257) 注意左閉右開。所以,超過這個範圍的整數,嚴格來說,是需要生成這樣的一個對象的。所以,就會出現下面的情況

&>&>&> a = 1
&>&>&> b = 1
&>&>&> id(a) == id(b)
True

&>&>&> c = 1000000
&>&>&> d = 1000000
&>&>&> id(c) == id(d)
False

而整數對象是一種不可變類型,也就是說,一旦你生成了一個257的整形對象,你這個對象保存的數字就是不能再變化的了。那麼我們對整數執行加法的時候,得到的結果和原來的對象是什麼關係呢?答案是,沒關係,結果是根據求和數值產生的一個全新的對象。即使全新的對象和原來數值一樣,也是不同的對象(除非數字在小整數對象池內)。例如:

&>&>&> e = c + 0
&>&>&> id(c) == id(e)
False
&>&>&> f = a + 0
&>&>&> id(a) == id(f)
True

而列表類型是一種可變類型。他提供了一些原地改變對象而不用生成新對象的方法,例如題主的s.reverse()。但同時,也可以生成一個新的對象儲存想要的結果,例如題主的s[::-1],或者reversed(s)。PS:注意reverse()和reversed的區別

以上!

=========

Updates

2014-11-16

經 @偷窺的貓Hatori 提醒,修改了一個筆誤。

2014-11-18

經 @龔撝撝 提醒,修改了一個筆誤。

=========

參考資料

Python 源碼閱讀

Python源碼--整數對象(PyIntObject)的內存池


在python中, parameter sent to function 使用的全部是 by object。

也就是,這無法通過by value或者 by reference 來定義。這是python的獨到之處。

如果object本身是immutable的,例如一個不是太長的整數,那麼你可以看作是傳值。因為每一次對這個object賦值,都會創建一個新的object,如下:

a=10

def function1(value):

value=20

print(value)

function1(a)

print(a)

結果是

20

10

雖然傳過去的是a這個object,但當function1對a賦值的時候,其實他並沒有改變a,而是創建了一個新的object,這個object叫做value了。global當中的a並沒有變。

如果object本身是mutable,例如一個list,因為每一次對這個object賦值,都會改變這個object本身。那麼就可以看作是傳reference。如下:

a=[10,11,12,13]

def function1(value):

value[1:3]=[]

print(value)

function1(a)

print(a)

結果是

[10,13]

[10,13]

。。答到一半,看了下題目好像答非所問了。

題目問的問題其實更簡單。

list.reverse 是一個in-place method。也就是說,reverse是在原來object上操作,而不會創造一個新的object。上面t=s,按照python傳object的標準,那麼就是t=s 是同一個object。.reverse作用在這個object上,那麼t,s都變了。他們只是名字而已。

而slicing [::] 這個,會創造一個新的object。所以。自然啦。

最好的辦法是deep copy


賦值只是傳址,切片的話就是copy值了


推薦閱讀:

Stack Overflow 2016年度 20個最佳Python問題(一)
python爬蟲之豆瓣音樂top250
如何訓練自己的編程思路
透過數據看 Github
數據分析修鍊歷程:你在哪一站?

TAG:Python |