2016-01-14 67 views
15

我想我理解的Python切片操作,但是當我嘗試更新切片名單,我糊塗了:更新切片列表

>>> foo = [1, 2, 3, 4] 
>>> foo[:1] = ['one'] # OK, foo updated 
>>> foo 
['one', 2, 3, 4] 
>>> foo[:][1] = 'two' # why foo not updated? 
>>> foo 
['one', 2, 3, 4] 
>>> foo[:][2:] = ['three', 'four'] # Again, foo not updated 
>>> foo 
['one', 2, 3, 4] 

爲什麼不富foo[:][1] = 'two'後更新?

更新:也許我沒有清楚地解釋我的問題。我知道切片時,會創建一個新列表。我懷疑爲什麼切片任務更新列表(例如foo[:1] = ['one']),但是如果有兩個切片層次,它不更新原始列表(例如foo[:][2:] = ['three', 'four'])。

+6

你以前是否使用過'numpy',或許? Numpy數組使用與Python列表不同的切片。 –

+0

請閱讀https://en.wikipedia.org/wiki/Value_(computer_science)#lrvalue –

+0

恭喜!你已經發現了[如何克隆或複製列表](http://stackoverflow.com/a/2612815/3904031)!然後詢問[複製程序有多深?](http://stackoverflow.com/a/26562235/3904031) – uhoh

回答

17

這是因爲python確實有而不是l-可以分配的值。相反,有些表達式具有不同的賦值形式。

一個foo[something]是一個語法糖:

foo.__getitem__(something) 

foo[something] = bar是相當不同的一個語法糖:

foo.__setitem__(something, bar) 

凡片只是something一個特殊的情況下,使foo[x:y]擴大爲

foo.__getitem__(slice(x, y, None)) 

foo[x:y] = bar擴展到

foo.__setitem__(slice(x, y, None), bar) 

現在用切片__getitem__返回一個新的列表在指定範圍的副本,所以修改它不會影響原數組。並且根據__setitem__的優點分配作品是一種不同的方法,可以簡單地做別的事情。

但是,特殊賦值處理僅適用於最外層的操作。成分是正常的表達。所以,當你寫

foo[:][1] = 'two' 

它被擴大到

foo.__getitem__(slice(None, None, None)).__setitem__(1, 'two') 

foo.__getitem__(slice(None, None, None))部分創建副本,該副本由__setitem__修改。但不是原始數組。

+0

但爲什麼局部切片,比如說foo [:1] = ['one'],起作用? – Liang

+0

**任何切片**都可以。這是_double_切片,沒有。 –

19

foo[:]foo的副本。你改變了副本。

+0

https://en.wikipedia.org/wiki/Value_(computer_science)#lrvalue值得在這裏添加, 'foo [:]'看上去可能是一樣的,但它是'foo [:] = ...中的一個左值,'foo [:] [2:] = ...中的一個右值',''是 –

+2

@qarma,它是Python中的左值和右值不正確。它沒有它們。在python中''= ='與'[]'是不同的運算符。 –

+0

是的,這是正確的,請在其餘的答案中提供解釋。 –

3

使用

foo[1] = 'two' 

foo[2:] = ['three', 'four'] 

和它的作品。

答案爲什麼在上面的註釋(因爲您使用的是複印件)

13

主要的事情這裏需要注意的是,foo[:]將返回其自身的副本,然後索引[1]將在應用這是返回

# indexing is applied on copied list 
(foo[:])[1] = 'two' 
    ^
copied list 

如果保留複製的列表的引用您可以查看此複製列表。因此,foo[:][1] = 'two'操作可以重新寫爲:現在

foo = [1, 2, 3, 4] 

# the following is similar to foo[:][1] = 'two' 

copy_foo = foo[:] 
copy_foo[1] = 'two' 

copy_foo已經改變:

print(copy_foo) 
# [1, 'two', 3, 4] 

但是,foo仍然是相同的:

print(foo) 
# [1, 2, 3, 4] 

你的情況,您沒有將中間結果命名爲foo列表foo[:],也就是說,你沒有提及它。在'two'分配爲foo[:][1] = 'two',後,中間複製列表不再存在