在 Python 中,在生成一个新的列表时,使用列表理解符号很简单。(List comprehensions
)
- 5. Data Structures — List Comprehensions — Python 3.10.0 Documentation
- 6. Expressions — Displays for lists, sets and dictionaries — Python 3.10.0 Documentation
在这篇文章中,我们将首先讨论以下内容
- 列表理解符号的基本类型
- 带有条件分支的列表理解符号,通过if来实现
- 与三元运算符的组合(类似if else的处理)。
zip()
,enumerate()
与这些结合起来- 嵌套列表包含符号
接下来,我们将用示例代码解释一套列表理解的符号。
- 集合符号(
Set comprehensions
) - 词典收录符号(
Dict comprehensions
) - 发电机类型(
Generator expressions
)
列表理解符号的基本类型
列表理解的符号写法如下。
[Expression for Any Variable Name in Iterable Object]
它通过一个任意的变量名获取一个可迭代对象的每个元素,如列表、元组或范围,并用一个表达式对其进行评估。一个以评估结果为元素的新列表被返回。
这里给出了一个例子,以及一个等价的for语句。
squares = [i**2 for i in range(5)]
print(squares)
# [0, 1, 4, 9, 16]
squares = []
for i in range(5):
squares.append(i**2)
print(squares)
# [0, 1, 4, 9, 16]
同样的过程也可以用map()来完成,但由于其简单明了,我们更喜欢用列表理解的符号。
带有条件分支的列表理解符号,通过if来实现
用if进行条件性分支也是可以的。将if写在后缀中,如下所示。
[Expression for Any Variable Name in Iterable Object if Conditional Expression]
只有条件表达式为真的可迭代对象的元素被表达式评估,并返回一个新的列表,其元素是结果。
你可以在条件表达式中使用任何变量名称。
这里给出了一个例子,以及一个等价的for语句。
odds = [i for i in range(10) if i % 2 == 1]
print(odds)
# [1, 3, 5, 7, 9]
odds = []
for i in range(10):
if i % 2 == 1:
odds.append(i)
print(odds)
# [1, 3, 5, 7, 9]
同样的过程也可以用filter()来完成,但由于其简单明了,我们更喜欢用列表理解的符号。
与三元运算符的组合(类似if else的处理)。
在上面的例子中,只有那些符合标准的元素被处理,而那些不符合标准的元素被排除在新的列表之外。
如果你想根据条件切换处理,或者你想对不满足条件的元素进行不同的处理,如if else,请使用三元运算符。
在Python中,三元运算符可以写成如下形式
Value When True if Conditional Expression else Value When False
这被用于列表理解符号的表达部分,如下所示。
[Value When True if Conditional Expression else Value When False for Any Variable Name in Iterable Object]
这里给出了一个例子,以及一个等价的for语句。
odd_even = ['odd' if i % 2 == 1 else 'even' for i in range(10)]
print(odd_even)
# ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']
odd_even = []
for i in range(10):
if i % 2 == 1:
odd_even.append('odd')
else:
odd_even.append('even')
print(odd_even)
# ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']
也可以用任意的变量名来写表达式的真和假值。
如果条件得到满足,就会进行一些处理,否则,原始可迭代对象的值将保持不变。
odd10 = [i * 10 if i % 2 == 1 else i for i in range(10)]
print(odd10)
# [0, 10, 2, 30, 4, 50, 6, 70, 8, 90]
与zip()和enumerate()的组合
经常在for语句中使用的有用的函数包括zip(),它结合了多个迭代变量,以及enumerate(),它返回一个值和它的索引。
当然,也可以用列表理解符号来使用zip()和enumerate()。这不是一种特殊的语法,如果你考虑到与for语句的对应关系,这并不困难。
zip()的例子。
l_str1 = ['a', 'b', 'c']
l_str2 = ['x', 'y', 'z']
l_zip = [(s1, s2) for s1, s2 in zip(l_str1, l_str2)]
print(l_zip)
# [('a', 'x'), ('b', 'y'), ('c', 'z')]
l_zip = []
for s1, s2 in zip(l_str1, l_str2):
l_zip.append((s1, s2))
print(l_zip)
# [('a', 'x'), ('b', 'y'), ('c', 'z')]
enumerate()的例子。
l_enu = [(i, s) for i, s in enumerate(l_str1)]
print(l_enu)
# [(0, 'a'), (1, 'b'), (2, 'c')]
l_enu = []
for i, s in enumerate(l_str1):
l_enu.append((i, s))
print(l_enu)
# [(0, 'a'), (1, 'b'), (2, 'c')]
在使用if时,其思路与之前相同。
l_zip_if = [(s1, s2) for s1, s2 in zip(l_str1, l_str2) if s1 != 'b']
print(l_zip_if)
# [('a', 'x'), ('c', 'z')]
每个元素也可以用来计算一个新元素。
l_int1 = [1, 2, 3]
l_int2 = [10, 20, 30]
l_sub = [i2 - i1 for i1, i2 in zip(l_int1, l_int2)]
print(l_sub)
# [9, 18, 27]
嵌套列表包含符号
像嵌套for循环一样,列表理解符号也可以被嵌套。
[Expression for Variable Name 1 in Iterable Object 1
for Variable Name 2 in Iterable Object 2
for Variable Name 3 in Iterable Object 3 ... ]
为方便起见,增加了换行和缩进,但对语法来说不是必需的;它们可以在一行中继续。
这里给出了一个例子,以及一个等价的for语句。
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
print(flat)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
flat = []
for row in matrix:
for x in row:
flat.append(x)
print(flat)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
也可以使用多个变量。
cells = [(row, col) for row in range(3) for col in range(2)]
print(cells)
# [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]
你也可以做条件性分支。
cells = [(row, col) for row in range(3)
for col in range(2) if col == row]
print(cells)
# [(0, 0), (1, 1)]
也可以对每个可迭代对象进行条件性分支。
cells = [(row, col) for row in range(3) if row % 2 == 0
for col in range(2) if col % 2 == 0]
print(cells)
# [(0, 0), (2, 0)]
集合符号(Set comprehensions)
将列表理解符号中的方括号[]改为大括号{},可以创建一个集合(集合型对象)。
{Expression for Any Variable Name in Iterable Object}
s = {i**2 for i in range(5)}
print(s)
# {0, 1, 4, 9, 16}
词典收录符号(Dict comprehensions)
词典(dict类型的对象)也可以用理解性符号生成。
{},并在表达式部分指定键和值为键:值。
{Key: Value for Any Variable Name in Iterable Object}
可以为键和值指定任何表达式。
l = ['Alice', 'Bob', 'Charlie']
d = {s: len(s) for s in l}
print(d)
# {'Alice': 5, 'Bob': 3, 'Charlie': 7}
要从一个键和值的列表中创建一个新的字典,使用 zip() 函数。
keys = ['k1', 'k2', 'k3']
values = [1, 2, 3]
d = {k: v for k, v in zip(keys, values)}
print(d)
# {'k1': 1, 'k2': 2, 'k3': 3}
发电机类型(Generator expressions)
如果列表理解符号中的方括号[]被用作圆括号(),则会返回一个生成器,而不是一个元组。这就是所谓的生成器表达式。
列表理解符号的例子。
l = [i**2 for i in range(5)]
print(l)
# [0, 1, 4, 9, 16]
print(type(l))
# <class 'list'>
生成器表达式的例子。如果你打印()这个生成器,它不会打印出它的内容,但如果你用for语句运行它,你可以得到内容。
g = (i**2 for i in range(5))
print(g)
# <generator object <genexpr> at 0x10af944f8>
print(type(g))
# <class 'generator'>
for i in g:
print(i)
# 0
# 1
# 4
# 9
# 16
生成器表达式还允许使用if以及列表理解符号进行条件分支和嵌套。
g_cells = ((row, col) for row in range(0, 3)
for col in range(0, 2) if col == row)
print(type(g_cells))
# <class 'generator'>
for i in g_cells:
print(i)
# (0, 0)
# (1, 1)
例如,如果使用列表理解符号生成一个有大量元素的列表,然后用for语句进行循环,如果使用列表理解符号,包含所有元素的列表将在开始时生成。另一方面,如果使用生成器表达式,每次重复循环时,都会逐一生成元素,从而减少内存的使用量。
如果生成器表达式是函数的唯一参数,圆括号()可以省略。
print(sum([i**2 for i in range(5)]))
# 30
print(sum((i**2 for i in range(5))))
# 30
print(sum(i**2 for i in range(5)))
# 30
至于处理速度,在处理所有元素时,列表理解符号往往比生成器符号快。
然而,当用all()或any()进行判断时,例如,当出现false或true时,就会确定结果,所以使用生成器表达式可能比使用列表理解符号要快。
没有元组的理解符号,但是如果你使用一个生成器表达式作为tuple()的参数,你可以生成一个理解符号的元组。
t = tuple(i**2 for i in range(5))
print(t)
# (0, 1, 4, 9, 16)
print(type(t))
# <class 'tuple'>