【问题标题】:Updating a sliced list更新切片列表
【发布时间】:2016-01-14 08:15:11
【问题描述】:

我以为我了解 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 在foo[:][1] = 'two' 之后没有更新?

更新:也许我没有清楚地解释我的问题。我知道切片时会创建一个新列表。我的疑问是为什么切片分配会更新列表(例如foo[:1] = ['one']),但如果有两个切片级别,它不会更新原始列表(例如foo[:][2:] = ['three', 'four'])。

【问题讨论】:

标签: python list


【解决方案1】:

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

【讨论】:

  • en.wikipedia.org/wiki/Value_(computer_science)#lrvalue 值得在这里添加,foo[:] 可能看起来相同,但它是foo[:] = ... 中的左值和foo[:] [2:] = ... 中的右值
  • @qarma,在 Python 中谈论左值和右值是不正确的。它没有它们。在 python 中,[]= 只是与 [] 不同的运算符。
  • 是的,这是正确的,请在答案中提供解释以供其他人查看。
  • @qarma:我认为你混淆了两个不同的人。 Jan Hudec 的回答已经解释了所有这些。
  • 但这不是你对foo[:][1] = 'two' 所做的,所以不是这样。
【解决方案2】:

这是因为 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__ 修改。但不是原来的数组。

【讨论】:

  • 但是为什么部分切片,比如 foo[:1]=['one'] 可以工作?
  • 任何切片都可以。它是 double 切片而不是。
  • stackoverflow.com/a/2612815/3099185 也许这也有帮助.....
【解决方案3】:

这里要注意的主要事情是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 列表来命名中间结果,也就是说,您没有保留对它的引用。在使用foo[:][1] = 'two' 执行对'two' 的分配后,中间复制列表不再存在

【讨论】:

    【解决方案4】:

    使用

    foo[1]  = 'two'
    

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

    它有效。

    答案在上面的评论中(因为您使用的是副本)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-14
      • 2016-11-22
      • 1970-01-01
      • 2011-05-12
      • 2017-06-24
      • 2015-02-19
      相关资源
      最近更新 更多