列表实战——就地改变列表——索引和切片赋值 | 第二部分 类型与操作 —— 第 8 章: 列表和字典 |《学习 python:强大的面向对象编程(第 5 版)》| python 技术论坛-380玩彩网官网入口
当使用列表时,可以通过分配一个特殊项(偏移量)或一整段区间(切片)来改变其内容:
>>> l = ['spam', 'spam', 'spam!']
>>> l[1] = 'eggs' # index assignment
>>> l
['spam', 'eggs', 'spam!']
>>> l[0:2] = ['eat', 'more'] # slice assignment: delete insert
>>> l # replaces items 0,1
['eat', 'more', 'spam!']
索引和切片赋值都是就地修改——它们直接修改被作用列表,而非为结果创建一个新列表对象。在python中的索引复制与在c语言和其他语言中的非常相似:python在指定偏移量处以一个新对象引用替换了原来的对象引用。
切片赋值(前面例子中的最后一个操作)一步替换了列表中的一整段。因为它会有点复杂,最好将它想象成两个步骤的集合:
- 删除。指定的在
=
左边的切片被删除了。 - 插入。在
=
右边的被包含在可迭代对象中的新项目被插入在列表的左边(在老的切片被删除的地方[19])。
这不是真正发生的事情,但它能帮助澄清为什么被插入的项目数量不比匹配被删除的项目数量。比如,假定有一个有两个或以上项目的列表l,赋值 l[1:2] = [4,5]
使用了两个项替换了一个——python首先删除了在[1:2]
的一个项的切片(从偏移量1到2,但不包括2),然后在被删除切片曾经呆的地方插入4和5.
这也解释了下面例子中的第二个切片赋值为什么真的是插入——python使用两个项替换了在[1:1]
处的空切片;也解释了第三个例子为什么真的是删除——python删除了切片(项在偏移量1处),然后什么也没插入:
>>> l = [1, 2, 3]
>>> l[1:2] = [4, 5] # replacement/insertion
>>> l
[1, 4, 5, 3]
>>> l[1:1] = [6, 7] # insertion (replace nothing)
>>> l
[1, 6, 7, 4, 5, 3]
>>> l[1:2] = [] # deletion (insert nothing)
>>> l
[1, 7, 4, 5, 3]
事实上,切片赋值一次性替换了一整段区间或“列”——即使这个列其替换是空的。因为正在被分配的序列长度不必匹配正在被分配的切片长度,所以切片赋值可以被用来替换(通过覆盖),扩展(通过插入),或收缩(通过删除)被作用列表。但坦率地讲,它是一个强大的操作,在实践中可能不会经常见到。通常在实践中,python程序员往往倾向于更多简单易记的方法来替换、插入、删除(比如,连接,和insert
, pop
和 remove
列表方法)。
另一方面,这个操作能被用作在列表前面的一种就地连接——根据下一章的方法介绍,列表的extend
在列表末尾更易记地做了一些事情:
>>> l = [1]
>>> l[:0] = [2, 3, 4] # insert all at :0, an empty slice at front
>>> l
[2, 3, 4, 1]
>>> l[len(l):] = [5, 6, 7] # insert all at len(l):, an empty slice at end
>>> l
[2, 3, 4, 1, 5, 6, 7]
>>> l.extend([8, 9, 10]) # insert all at end, named method
>>> l
[2, 3, 4, 1, 5, 6, 7, 8, 9, 10]
note19:
当值和被分配的切片重叠时,这个描述需要详细阐述:比如,l[2:5]=l[3:6]
工作正常,因为要被插入的值在左边的删除操作发生前被获取。
>>> l
['a', 'b', 'c', 'd', 'e', 'f', 'h']
>>> l[2:5]
['c', 'd', 'e']
>>> l[3:6]
['d', 'e', 'f']
>>> l[2:5]=l[3:6]
>>> l
['a', 'b', 'd', 'e', 'f', 'f', 'h']