切片整个列表和直接分配的切片分配有什么区别?

我在许多地方看到使用list s的片分配。 在使用(非默认)索引时,我能够理解它的用法,但我不能理解它的用法,如:

 a_list[:] = ['foo', 'bar'] 

与那个有什么不同?

 a_list = ['foo', 'bar'] 

 a_list = ['foo', 'bar'] 

在内存中创build一个新list ,并在其中指定名称a_list 。 这与a_list之前指出的是无关的。

 a_list[:] = ['foo', 'bar'] 

slice作为索引调用a_list对象的__setitem__方法,并在内存list创build一个新list作为值。

__setitem__计算slice以找出它所代表的索引,并在它传递的值上调用它。 然后迭代该对象,将slice指定范围内的每个索引设置为该对象的下一个值。 对于list s,如果由slice指定的范围与iterable的长度不同,则调整list的大小。 这允许你做一些有趣的事情,比如删除一个列表的部分:

 a_list[:] = [] # deletes all the items in the list, equivalent to 'del a_list[:]' 

或者在列表中间插入新的值:

 a_list[1:1] = [1, 2, 3] # inserts the new values at index 1 in the list 

但是,对于“扩展切片”,其中step不是一个,迭代必须是正确的长度:

 >>> lst = [1, 2, 3] >>> lst[::2] = [] Traceback (most recent call last): File "<interactive input>", line 1, in <module> ValueError: attempt to assign sequence of size 0 to extended slice of size 2 

切片分配到a_list的主要内容是:

  1. a_list必须已经指向一个对象
  2. 该对象被修改,而不是将a_list指向新对象
  3. 该对象必须支持具有slice索引的__setitem__
  4. 右侧的对象必须支持迭代
  5. 没有名字指向右侧的对象。 如果没有其他的引用(例如当它是一个文字,如你的例子),它会被引用计数完成迭代后不存在。

差异是相当巨大的! 在

 a_list[:] = ['foo', 'bar'] 

您修改了绑定到名称a_list的现有列表。 另一方面,

 a_list = ['foo', 'bar'] 

给名字a_list分配一个新的列表。

也许这将有助于:

 a = a_list = ['foo', 'bar'] # another name for the same list a_list = ['x', 'y'] # reassigns the name a_list print a # still the original list a = a_list = ['foo', 'bar'] a_list[:] = ['x', 'y'] # changes the existing list bound to a print a # a changed too since you changed the object 

通过分配给a_list[:]a_list仍然引用相同的列表对象,并修改了内容。 通过分配给a_lista_list现在引用一个新的列表对象。

看看它的id

 >>> a_list = [] >>> id(a_list) 32092040 >>> a_list[:] = ['foo', 'bar'] >>> id(a_list) 32092040 >>> a_list = ['foo', 'bar'] >>> id(a_list) 35465096 

正如你所看到的,它的id不会随着切片分配版本而改变。


两者之间的差异可能会导致完全不同的结果,例如,当列表是函数的参数时:

 def foo(a_list): a_list[:] = ['foo', 'bar'] a = ['original'] foo(a) print(a) 

有了这个, a被修改了,但是如果使用a_list = ['foo', 'bar']来代替a ,那么a保持原来的值。