列表概述

在 Python 中,列表是一个有序的项(或元素)集合。它是一种非常通用的结构,列表元素不必是相同型:你可以将数字、字母、字符串和嵌套列表放在同一个列表中。

示例:

list1 = []                      # 一个新的空列表
list2 = [1, 2, 3, "cat"]        # 一个新的非空列表,包含不同型的元素
list1.append("cat")             # 在列表的末尾添加一个元素
list1.extend(["dog", "mouse"])  # 添加多个元素
list1.insert(0, "fly")          # 在开头插入元素
list1[0:0] = ["cow", "doe"]     # 在开头添加元素
doe = list1.pop(1)              # 移除索引为 1 的项
if "cat" in list1:              # 判断元素是否在列表中
  list1.remove("cat")           # 移除元素
#list1.remove("elephant") - 会抛出错误
for item in list1:              # 遍历列表中的每个元素
  print(item)
print("Item count:", len(list1))# 列表的长度(即元素个数)
list3 = [6, 7, 8, 9]
for i in range(0, len(list3)):  # 读写遍历,访问每个元素
  list3[i] += 1                 # 通过索引访问并修改元素
last = list3[-1]                # 最后一个元素
nextToLast = list3[-2]          # 倒数第二个元素
isempty = len(list3) == 0       # 判断列表是否为空
set1 = set(["cat", "dog"])      # 从列表创建一个集合
list4 = list(set1)              # 从集合获取一个列表
list5 = list4[:]                # 浅拷贝列表
list4equal5 = list4 == list5    # 判断两个列表是否相等(按值比较)
list4refEqual5 = list4 is list5 # 判断两个列表是否引用同一个对象(按引用比较)
list6 = list4[:]
del list6[:]                    # 清空列表
list7 = [1, 2] + [2, 3, 4]      # 列表连接
print(list1, list2, list3, list4, list5, list6, list7)
print(list4equal5, list4refEqual5)
print(list3[1:3], list3[1:], list3[:2]) # 列表切片
print(max(list3), min(list3), sum(list3)) # 聚合函数
print([x for x in range(10)])   # 列表推导式
print([x for x in range(10) if x % 2 == 1])
print([x for x in range(10) if x % 2 == 1 if x < 5])
print([x + 1 for x in range(10) if x % 2 == 1])
print([x + y for x in '123' for y in 'abc'])

列表创建

在 Python 中有两种不同的方式来创建列表。第一种是通过赋值(“静态”方式),第二种是使用列表推导式(“动态”方式)。

静态创建

要创建一个静态列表,只需将元素写在方括号内。例如:

[1, 2, 3, "This is a list", 'c', Donkey("kong")]

观察:

  • 列表包含不同数据类型的元素:整数、字符串和 Donkey
  • 可以动态创建对象并将其添加到列表中,最后一个项是一个新的 Donkey 实例。
  • 创建一个新列表,列表的元素由非字面量表达式构成:
a = 2
b = 3
myList = [a + b, b + a, len(["a", "b"])]

列表推导式

使用列表推导式,你描述列表创建的过程。列表推导式分为两部分:第一部分是每个元素的“外观”,第二部分是如何获取这些元素。

例如,假设我们有一个包含单词的列表:

listOfWords = ["this", "is", "a", "list", "of", "words"]

要从每个单词中提取第一个字母并使用列表推导式创建一个新的列表,可以这样做:

>>> listOfWords = ["this", "is", "a", "list", "of", "words"]
>>> items = [word[0] for word in listOfWords]
>>> print(items)
['t', 'i', 'a', 'l', 'o', 'w']

列表推导式支持多个 for 语句。它会按顺序遍历所有对象,如果一个对象比其他对象长,它会循环访问较短的对象。

>>> item = [x + y for x in 'cat' for y in 'pot']
>>> print(item)
['cp', 'co', 'ct', 'ap', 'ao', 'at', 'tp', 'to', 'tt']

列表推导式也支持 if 语句,只将满足特定条件的成员包含到列表中:

>>> print([x + y for x in 'cat' for y in 'pot'])
['cp', 'co', 'ct', 'ap', 'ao', 'at', 'tp', 'to', 'tt']
>>> print([x + y for x in 'cat' for y in 'pot' if x != 't' and y != 'o'])
['cp', 'ct', 'ap', 'at']
>>> print([x + y for x in 'cat' for y in 'pot' if x != 't' or y != 'o'])
['cp', 'co', 'ct', 'ap', 'ao', 'at', 'tp', 'tt']

在 Python 2.x 中,列表推导式没有定义作用域。任何在评估过程中绑定的变量会保持绑定到它们在评估完成时的值。在 Python 3.x 中,列表推导式使用局部变量:

>>> print(x, y)   # Python 2 输入
t t              # Python 2 输出

>>> print(x, y)   # Python 3 输入
NameError: name 'x' is not defined  # Python 3 输出错误,因为 x 和 y 未泄漏

这与将列表推导式展开成显式嵌套的多个 for 语句和 0 或多个 if 语句的效果相同。

列表创建快捷方式

你可以初始化一个列表,给每个元素指定一个初始值:

>>> zeros = [0] * 5
>>> print(zeros)
[0, 0, 0, 0, 0]

这适用于任何数据类型

>>> foos = ['foo'] * 3
>>> print(foos)
['foo', 'foo', 'foo']

但是有个注意事项。当通过乘法生成新列表时,Python 会按引用复制每个元素。这对可变项(例如二维数组,其中每个元素本身是一个列表)构成了问题。你可能认为通过以下方式生成二维数组很简单:

listoflists = [[0] * 4] * 5

这确实可行,但可能并不如你所期望:

>>> listoflists = [[0] * 4] * 5
>>> print(listoflists)
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> listoflists[0][2] = 1
>>> print(listoflists)
[[0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0]]

发生的情况是,Python 使用相同的引用来表示内层列表作为外层列表的元素。另一种理解这个问题的方法是检查 Python 如何看到上述定义:

>>> innerlist = [0] * 4
>>> listoflists = [innerlist] * 5
>>> print(listoflists)
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> innerlist[2] = 1
>>> print(listoflists)
[[0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0]]

如果上述效果不是你想要的,解决这个问题的一种方法是使用列表推导式:

>>> listoflists = [[0] * 4 for i in range(5)]
>>> print(listoflists)
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> listoflists[0][2] = 1
>>> print(listoflists)
[[0, 0, 1, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

列表大小

要找到列表的长度,可以使用内置的 len() 方法:

>>> len([1, 2, 3])
3
>>> a = [1, 2, 3, 4]
>>> len(a)
4

合并列表

列表可以通过多种方式合并。最简单的方式就是直接“添加”它们。例如:

>>> [1, 2] + [3, 4]
[1, 2, 3, 4]

另一种合并列表的方式是使用 extend。如果你需要在 lambda 中合并列表,extend 是最合适的方法。

>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> a.extend(b)
>>> print(a)
[1, 2, 3, 4, 5, 6]

向列表添加一个值的另一种方式是使用 append。例如:

>>> p = [1, 2]
>>> p.append([3, 4])
>>> p
[1, 2, [3, 4]]
>>> # 或者
>>> print(p)
[1, 2, [3, 4]]

然而,[3, 4] 是列表的一个元素,而不是列表的一部分。append 始终仅将一个元素添加到列表的末尾。所以,如果目的是将两个列表连接起来,始终使用 extend

获取列表的片段(切片)

连续切片

与字符串似,列表也可以通过索引和切片来访问:

>>> list = [2, 4, "usurp", 9.0, "n"]
>>> list[2]
'usurp'
>>> list[3:]
[9.0, 'n']

就像字符串的切片是子字符串一样,列表的切片是一个子列表。然而,列表与字符串的不同之处在于,我们可以将新值分配给列表中的项:

>>> list[1] = 17
>>> list
[2, 17, 'usurp', 9.0, 'n']

我们还可以给列表的切片赋予新值,切片的长度不必相同:

>>> list[1:4] = ["opportunistic", "elk"]
>>> list
[2, 'opportunistic', 'elk', 'n']

甚至可以通过将空切片赋值来将项添加到列表的开头:

>>> list[:0] = [3.14, 2.71]
>>> list
[3.14, 2.71, 2, 'opportunistic', 'elk', 'n']

同样,通过在列表的末尾指定空切片,可以将项添加到列表的末尾:

>>> list[len(list):] = ['four', 'score']
>>> list
[3.14, 2.71, 2, 'opportunistic', 'elk', 'n', 'four', 'score']

你还可以完全更改列表的内容:

>>> list[:] = ['new', 'list', 'contents']
>>> list
['new', 'list', 'contents']

列表赋值语句的右侧可以是任何可迭代型:

>>> list[:2] = ('element', ('t',), [])
>>> list
['element', ('t',), [], 'contents']

通过切片你可以创建列表的副本,因为切片返回一个新列表:

>>> original = [1, 'element', []]
>>> list_copy = original[:]
>>> list_copy
[1, 'element', []]
>>> list_copy.append('new element')
>>> list_copy
[1, 'element', [], 'new element']
>>> original
[1, 'element', []]

但是要注意,这是一个浅拷贝,包含对原始列表元素的引用,因此对可变型要小心:

>>> list_copy[2].append('something')
>>> original
[1, 'element', ['something']]

非连续切片

也可以获取数组的非连续部分。如果想获取列表的每隔 n 项,可以使用 :: 操作符。语法是 a:b:n,其中 ab 是要操作的切片的开始和结束索引。

>>> list = [i for i in range(10)]
>>> list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list[::2]
[0, 2, 4, 6, 8]
>>> list[1:7:2]
[1, 3, 5]

比较列表

列表可以通过 == 操作符进行比较:

>>> [1, 2] == [1, 2]
True
>>> [1, 2] == [3, 4]
False

列表还可以使用小于操作符进行比较,采用字典顺序(lexicographical order):

>>> [1, 2] < [2, 1]
True
>>> [2, 2] < [2, 1]
False
>>> ["a", "b"] < ["b", "a"]
True

序列

排序简要说明:

list1 = [2, 3, 1, 'a', 'B']
list1.sort()                                  # list1 原地修改,区分大小写
list2 = sorted(list1)                         # list1 未修改,从 Python 2.4 起
list3 = sorted(list1, key=lambda x: x.lower()) # 不区分大小写; 如果列表中有非字符串元素,.lower() 会报错
list4 = sorted(list1, reverse=True)           # 降序排序
print(list1, list2, list3, list4)

使用 sort 方法排序列表非常容易:

>>> list1 = [2, 3, 1, 'a', 'b']
>>> list1.sort()
>>> list1
[1, 2, 3, 'a', 'b']

注意,sort() 方法会原地排序列表,并且返回 None 来强调这个副作用。

如果你使用 Python 2.4 或更高版本,还可以使用一些额外的排序参数:

sort(cmp, key, reverse)
cmp : 用于确定两个元素之间相对顺序的函数
key : 用于获取每个元素的排序值的函数
reverse : sort(reverse=True) 或 sort(reverse=False)

Python 还提供了一个 sorted() 函数

>>> list1 = [5, 2, 3, 'q', 'p']
>>> sorted(list1)
[2, 3, 5, 'p', 'q']
>>> list1
[5, 2, 3, 'q', 'p']

sort() 方法不同,sorted(list) 不会在原地排序列表,而是返回一个新的排序列表。像 sort() 方法一样,sorted() 也接受 reverse 参数。

链接:

迭代

只读迭代列表

遍历列表中的每个元素:

list1 = [1, 2, 3, 4]
for item in list1:
  print(item)

可写迭代列表

遍历列表并修改其中的元素:

list1 = [1, 2, 3, 4]
for i in range(0, len(list1)):
  list1[i] += 1  # 根据需要修改列表中的元素
print(list1)

从一个数字到另一个数字,步长为 n:

for i in range(1, 13 + 1, 3):  # 从 1 到 13,步长为 3
  print(i)

for i in range(10, 5 - 1, -1):  # 从 10 到 5,步长为 -1
  print(i)

对列表中的元素满足条件的进行迭代(过滤):

for item in list1:
  if not condition(item):
    continue
  print(item)

删除

删除列表中指定索引的项(参见 pop(i)):

list1 = [1, 2, 3, 4]
list1.pop()  # 删除最后一个元素
list1.pop(0)  # 删除第一个元素,即索引为 0 的元素
print(list1)

通过值删除元素:

list1 = ["a", "a", "b"]
list1.remove("a")  # 删除第一个 "a"
print(list1)

通过过滤选择创建一个新列表:

list1 = [1, 2, 3, 4]
newlist = [item for item in list1 if item > 2]
print(newlist)

使用 [:] 更新列表,保留符合条件的项:

list1 = [1, 2, 3, 4]
sameList = list1
list1[:] = [item for item in list1 if item > 2]
print(sameList, sameList is list1)

使用一个独立的函数定义更复杂的条件:

list1 = [1, 2, 3, 4]
def keepingCondition(item):
  return item > 2
sameList = list1
list1[:] = [item for item in list1 if keepingCondition(item)]
print(sameList, sameList is list1)

在迭代列表时删除项通常会导致意外的结果,除非小心使用索引:

list1 = [1, 2, 3, 4]
index = len(list1)
while index > 0:
  index -= 1
  if not list1[index] < 2:
    list1.pop(index)

聚合

Python 为列表提供了一些内置的聚合函数,包括最小值、最大值和总和:

list = [1, 2, 3, 4]
print(max(list), min(list), sum(list))
average = sum(list) / float(len(list))  # 确保列表非空
# 上面的 float 确保进行浮点数除法,而不是整数除法。
print(average)

maxmin 函数也适用于字符串列表,返回按字母顺序排列的最大值和最小值:

list = ["aa", "ab"]
print(max(list), min(list))  # 输出 "ab aa"

复制

创建浅拷贝:

list1 = [1, 'element']
list2 = list1[:]  # 使用 "[:]" 复制
list2[0] = 2  # 只影响 list2,不影响 list1
print(list1[0])  # 显示 1

对比:

list1 = [1, 'element']
list2 = list1
list2[0] = 2  # 修改原始列表
print(list1[0])  # 显示 2

上面这并不是深拷贝,这样的后果是:

list1 = [1, [2, 3]]  # 注意第二项是一个嵌套列表
list2 = list1[:]  # 浅拷贝
list2[1][0] = 4  # 同时修改了 list1 的第二项
print(list1[1][0])  # 显示 4 而不是 2

创建深拷贝:

import copy
list1 = [1, [2, 3]]  # 第二项是嵌套列表
list2 = copy.deepcopy(list1)  # 深拷贝
list2[1][0] = 4  # list1 的第二项不受影响
print(list1[1][0])  # 显示 2

清空列表

清空列表:

del list1[:]  # 清空列表
list1 = []    # 不是清空,而是重新赋值为空列表

使用正确的方法清空列表在作为参数传递时会有所不同:

def workingClear(ilist):
  del ilist[:]  # 清空列表
def brokenClear(ilist):
  ilist = []  # 让 ilist 指向新列表,丢失对参数列表的引用

list1 = [1, 2]
workingClear(list1)
print(list1)

list1 = [1, 2]
brokenClear(list1)
print(list1)

移除重复项

删除列表中的重复项(保留唯一项):

如果列表中的每个项是可哈希的,使用列表推导式,它非常快速:

list1 = [1, 4, 4, 5, 3, 2, 3, 2, 1]
seen = {}
list1[:] = [seen.setdefault(e, e) for e in list1 if e not in seen]

如果列表中的项不可哈希,仍然可以用列表来保存已访问的项:

list1 = [1, 4, 4, ["a", "b"], 5, ["a", "b"], 3, 2, 3, 2, 1]
seen = []
for i in range(len(list1) - 1, -1, -1):
  if list1[i] in seen:
    list1.pop(i)
  seen.append(list1[i])

列表方法

append(x)

将项 x 添加到列表的末尾。

>>> list = [1, 2, 3]
>>> list.append(4)
>>> list
[1, 2, 3, 4]

pop(i)

删除索引为 i 的项并返回它。如果没有给定索引,删除最后一项并返回它。

>>> list = [1, 2, 3, 4]
>>> a = list.pop(0)
>>> list
[2, 3, 4]
>>> a
1
>>> b = list.pop()
>>> list
[2, 3]
>>> b
4

操作符

+ 用于连接两个列表。

* 用于通过将给定列表重复指定次数来创建一个新列表。

in 用于检查值是否在列表中,返回 TrueFalse

>>> list = [1, 2, 3, 4]
>>> if 3 in list:
>>>    ....
>>> l = [0, 1, 2, 3, 4]
>>> 3 in l
True
>>> 18 in l
False
>>> for x in l:
>>>    print(x)
0
1
2
3
4

列表差异

获取两个列表之间的差异:

a = [0, 1, 2, 3, 4, 4]
b = [1, 2, 3, 4, 4, 5]
print([item for item in a if item not in b])  # [0]

列表交集

获取两个列表的交集:

a = [0, 1, 2, 3, 4, 4]
b = [1, 2, 3, 4, 4, 5]
dif = [item for item in a if item not in b]
print([item for item in a if item not in dif])  # [1, 2, 3, 4, 4]

练习

  1. 使用列表推导式构造列表 ['ab', 'ac', 'ad', 'bb', 'bc', 'bd']
  2. 对上面的列表使用切片构造列表 ['ab', 'ad', 'bc']
  3. 使用列表推导式构造列表 ['1a', '2a', '3a', '4a']
  4. 同时移除元素 '2a' 并打印。
  5. 复制上面的列表并将 '2a' 添加回列表中,确保原始列表仍然没有 '2a'
  6. 使用列表推导式构造列表 ['abe', 'abf', 'ace', 'acf', 'ade', 'adf', 'bbe', 'bbf', 'bce', 'bcf', 'bde', 'bdf']

解决方案

  1. List1 = [a + b for a in 'ab' for b in 'bcd']
  2. List2 = List1[::2]
  3. List3 = [a + b for a in '1234' for b in 'a']
  4. print(List3.pop(List3.index('3a')))
  5. List4 = List3[:] and List4.insert(2, '3a')
  6. List5 = [a + b + c for a in 'ab' for b in 'bcd' for c in 'ef']
Last modified: Thursday, 30 January 2025, 11:33 PM