2016-01-14 37 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',后,中间复制列表不再存在