list是一种可变的集合,其中的元素按_顺序_排列。和大多数集合一样(参见内置的tuple、dict和set),列表可以保存对任意(或多种)数据类型的引用,甚至可以保存其他列表。在 Python 中,它们被视为一种序列类型,可以通过切片表示法整体或部分地复制。和任何序列一样,lists中的元素既可以用从左侧数的0-based index来引用,也可以用从右侧数的-1-based index来引用。
列表同时支持通用序列操作和可变序列操作,例如min(<list>)/max(<list>)、<list>.index()、<list>.append()和<list>.reverse()。可以使用for item in <list>结构来迭代其中的元素;如果需要同时用到元素和元素下标,则可以使用for index, item in enumerate(<list>)。
Python 提供了许多用于处理列表的实用方法。
由于列表是可变的,列表方法会修改传给方法的原列表对象。如果不希望发生修改,就需要通过slice或<list>.copy()对原list做一次shallow copy(_至少_如此)。
可以通过<list>.append(<item>)把元素添加到现有列表的末尾:
>>> numbers = [1, 2, 3]
>>> numbers.append(9)
>>> numbers
[1, 2, 3, 9]
<list>.insert(<index>, <item>)不是_追加_,而是把元素添加到列表中的_特定下标_处。<index>是你希望新元素出现在其_之前_的那个元素的下标。<item>是要插入的元素。
注意:如果<index>为 0,元素会被添加到列表开头。如果<index>大于列表的最后一个下标,元素会被添加到最后的位置,相当于使用<list>.append(<item>)。
>>> numbers = [1, 2, 3]
>>> numbers.insert(0, -2)
>>> numbers
[-2, 1, 2, 3]
>>> numbers.insert(1, 0)
>>> numbers
[-2, 0, 1, 2, 3]
iterable可以通过<list>.extend(<iterable>)与现有列表_合并_(把两者拼接起来)。<list>.extend(<iterable>)会_解包_传入的可迭代对象,按相同顺序把它的元素添加到目标列表的末尾。在这种情况下使用<list>.append(<item>),则会把整个可迭代对象作为_单个元素_添加进去。
>>> numbers = [1, 2, 3]
>>> other_numbers = [5, 6, 7]
>>> numbers.extend(other_numbers)
>>> numbers
[1, 2, 3, 5, 6, 7]
>>> numbers.extend([8, 9])
>>> numbers
[1, 2, 3, 5, 6, 7, 8, 9]
可以用<list>.remove(<item>)从列表中删除一个元素。如果该元素不在列表中,<list>.remove(<item>)会抛出ValueError。
>>> numbers = [1, 2, 3]
>>> numbers.remove(2)
>>> numbers
[1, 3]
# Trying to remove a value that is not in the list throws a ValueError.
>>> numbers.remove(0)
ValueError: list.remove(x): x not in list
另一种方式是使用<list>.pop(<index>)方法,它会删除并return一个元素供你使用。
<list>.pop(<index>)接受一个可选形参:要删除并返回的元素的index。如果没有指定可选的<index>实参,就删除并返回列表的最后一个元素。如果<index>大于列表的最后一个下标,则会抛出IndexError。
>>> numbers = [1, 2, 3]
>>> numbers.pop(0)
1
>>> numbers
[2, 3]
>>> numbers.pop()
3
>>> numbers
[2]
# This will throw an error because there is only index 0.
>>> numbers.pop(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: pop index out of range
可以通过<list>.clear()删除list中的所有元素。它不接受任何形参。
>>> numbers = [1, 2, 3]
>>> numbers.clear()
>>> numbers
[]
列表元素的顺序可以用<list>.reverse()_原地_反转。这会修改原列表。
>>> numbers = [1, 2, 3]
>>> numbers.reverse()
>>> numbers
[3, 2, 1]
借助<list>.sort()可以_原地重新排列列表。默认的排序顺序是从左到右的_升序。Python 文档还提供了更多排序技巧和手法。
从 2002 年到 2022 年,Python 内部使用一种名为Timsort的算法来排列列表,但从Python 3.11起改用了Powersort。你可以在 GitHub 的issue 78742中读到 Python 核心团队关于这一变动的更多细节和讨论。
关于该算法的技术细节,参见 J. Ian Munro 和 Sebastian Wild 的论文 Nearly-Optimal Mergesorts: Fast, Practical Sorting Methods That Optimally Adapt to Existing Runs
>>> names = ["Tony", "Natasha", "Thor", "Bruce"]
# The default sort order is *ascending*.
>>> names.sort()
>>> names
["Bruce", "Natasha", "Thor", "Tony"]
要按_降序_排列列表,向该方法传入reverse=True实参:
>>> names = ["Tony", "Natasha", "Thor", "Bruce"]
>>> names.sort(reverse=True)
>>> names
["Tony", "Thor", "Natasha", "Bruce"]
如果不想修改原list,可以使用内置函数sorted()和reversed()。sorted(<list>)会返回一个排好序的_副本_,并接受与<list>.sort()相同的形参。reversed(<list>)返回一个_迭代器_,按相反顺序产出列表中的元素。Iterators会在另一个练习中详细介绍。
借助<list>.count(<item>)可以找出某个元素在列表中出现的次数。它返回<item>作为元素在列表中出现的总次数。
>>> items = [1, 4, 7, 8, 2, 9, 2, 1, 1, 0, 4, 3]
>>> items.count(1)
3
<list>.index(<item>)会返回传入元素_第一次出现_处的下标。如果该元素从未出现,则会抛出ValueError。下标从左侧开始按0-based计数,也就是说第一个元素的位置是下标0。也支持从右侧计数,从下标-1开始。
>>> items = [7, 4, 1, 0, 2, 5]
>>> items.index(4)
1
>>> items.index(10)
ValueError: 10 is not in list
提供start和end下标可以在列表的特定区间内搜索:
>>> names = ["Tina", "Leo", "Thomas", "Tina", "Emily", "Justin"]
>>> names.index("Tina")
0
>>> names.index("Tina", 2, 5)
3
如果不需要元素的确切位置,用内置的in运算符来判断某个元素是否在列表中会更高效。
>>> names = ["Tina", "Leo", "Thomas", "Tina", "Emily", "Justin"]
>>> "Thomas" in names
True
请记住,Python 中的变量是指向底层对象的名称。在程序的运行过程中,名称可以绑定或重新绑定到不同的对象。把一个list对象赋给一个新的变量_名称,并不会复制该对象,也不会复制它引用的任何数据。新名称和旧名称都会指向同一个list对象。此外,列表是一种_容器_类型对象:为了节省空间,容器只保存对成员元素的_引用,而不是元素本身。这种“优化”可能给不谨慎的人带来意想不到的后果。
<list>.copy()会创建一个新的list对象,但不会为所引用的列表_元素_创建新对象,这种副本是“浅”的。当你想从其中一个list对象添加或删除元素、而不影响另一个时,shallow copy通常就够了。但如果list的_底层元素_有可能被意外修改(从而修改所有相关的浅拷贝),就应该使用copy模块中的copy.deepcopy(),为所有引用和对象创建一个完整的、“深”的副本。
关于名称、值、列表以及嵌套列表行为的详细解释,可以看看 Ned Batchelder 写的这篇优秀博文:Names and values: making a game board。Shallow vs Deep Copying of Python Objects 也对复制时的各种考量做了很好的梳理。