项目 | 描述 |
---|---|
流畅的 Python | Luciano Ramalho 著 / 安道 吴珂 译 |
搜索引擎 | Bing |
项目 | 描述 |
---|---|
Python | 3.10.6 |
列表推导式是 Python 官方提供的一种相对更为快捷的创建列表的方式,使用列表表达式你将能够写出更加优雅(更具有可读性,也相对更为简洁)的代码。当然,你也需要为此付出一定的代价,你需要掌握它。
创建一个包含 0~100 内的偶数的平方的列表。对此,我们将以列表推导式或常规方式来进行实现。
常规方式
arr = []
for i in range(0, 101, 2):
arr.append(i ** 2)
print(arr)
执行结果
[0, 4, 16, 36, 64, 100, 144, 196, 256, 324, 400, 484, 576, 676, 784, 900, 1024, 1156, 1296, 1444, 1600, 1764, 1936, 2116, 2304, 2500, 2704, 2916, 3136, 3364, 3600, 3844, 4096, 4356, 4624, 4900, 5184, 5476, 5776, 6084, 6400, 6724, 7056, 7396, 7744, 8100, 8464, 8836, 9216, 9604, 10000]
列表推导式
arr = [i ** 2 for i in range(0, 101, 2)]
print(arr)
执行结果
[0, 4, 16, 36, 64, 100, 144, 196, 256, 324, 400, 484, 576, 676, 784, 900, 1024, 1156, 1296, 1444, 1600, 1764, 1936, 2116, 2304, 2500, 2704, 2916, 3136, 3364, 3600, 3844, 4096, 4356, 4624, 4900, 5184, 5476, 5776, 6084, 6400, 6724, 7056, 7396, 7744, 8100, 8464, 8836, 9216, 9604, 10000]
补充
常规方式与列表表达式对该目标的实现在优雅程度上相差无几,这种差距难以在较为简单的编程任务中体现。在复杂的编程任务中,使用列表推导式来进行目标的达成,于人于己都将是一种享受。
列表表达式通过在中括号内使用 for 循环语句来指定序列中应创建元素的个数。对此,请参考如下示例:
arr = [9 * 1 for i in range(100)]
print(arr)
print(len(arr))
执行结果
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
100
当然,你可以使用表达式来对循环过程中发生变化的循环变量加以利用。对此,请参考如下示例:
arr = [i + i for i in range(30)]
print(arr)
执行效果
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58]
下述一般式为常见的列表推导式的运用的抽象,根据需要你可以对其进行扩展及修改。对于具体的使用方法,请参考后文。
# 基本形式
[expr for item in sequence]
# 基本形式-判断
[expr for item in sequence if condition]
# 基本形式-笛卡尔积
[expr for item1 in sequence for item2 in sequence]
# 基本形式-拆解
[expr for items in sequence for item in sequence]
其中:
项目 | 描述 |
---|---|
expr | 用于产生序列中的元素的表达式。 |
sequence | 被遍历的序列对象(元祖,列表等)。 |
item | 循环变量(被遍历的序列对象中的元素)。 |
condition | 布尔表达式(结果值为一个布尔值)。 |
在列表推导式中,我们可以使用判断语句来对元素进行筛选。对此,请参考如下示例:
创建包含 0~100 范围内所有为三的倍数的数值的列表
arr = [i for i in range(101) if i % 3 == 0]
print(arr)
执行结果
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99]
在列表推导式中,你仍可以使用逻辑推导式来进行更细致的判断。对此,其参考如下示例:
创建包含 0~100 范围内所有为三及五的倍数的数值的列表
arr = [i for i in range(101) if i % 3 == 0 and i % 5 == 0]
print(arr)
执行效果
[0, 15, 30, 45, 60, 75, 90]
上述内容引用自 维基百科
接下来,我们将使用列表推导式来实现一副 扑克牌。
patterns = ['heart', 'club', 'spade', 'diamond']
symbols = [i for i in range(2, 11)] + list('AJOK')
cards = [(symbol, pattern) for symbol in symbols for pattern in patterns]
print(cards)
print(len(cards))
执行结果
[(2, 'heart'), (2, 'club'), (2, 'spade'), (2, 'diamond'), (3, 'heart'), (3, 'club'), (3, 'spade'), (3, 'diamond'), (4, 'heart'), (4, 'club'), (4, 'spade'), (4, 'diamond'), (5, 'heart'), (5, 'club'), (5, 'spade'), (5, 'diamond'), (6, 'heart'), (6, 'club'), (6, 'spade'), (6, 'diamond'), (7, 'heart'), (7, 'club'), (7, 'spade'), (7, 'diamond'), (8, 'heart'), (8, 'club'), (8, 'spade'), (8, 'diamond'), (9, 'heart'), (9, 'club'), (9, 'spade'), (9, 'diamond'), (10, 'heart'), (10, 'club'), (10, 'spade'), (10, 'diamond'), ('A', 'heart'), ('A', 'club'), ('A', 'spade'), ('A', 'diamond'), ('J', 'heart'), ('J', 'club'), ('J', 'spade'), ('J', 'diamond'), ('O', 'heart'), ('O', 'club'), ('O', 'spade'), ('O', 'diamond'), ('K', 'heart'), ('K', 'club'), ('K', 'spade'), ('K', 'diamond')]
52
通过在列表推导式中使用多个 for 循环语句并在合适的位点使用合适的循环变量(列表推导式中,相邻的两个 for 循环语句总会使用到同一个变量),我们将能够达到嵌套循环达到的效果。接下来,我们将使用这个特性来将一个二维列表拆解为一维列表。
# 这是一个二维列表
target = [[0, 1, 2],
[3, 4, 5],
[6, 7, 8]]
# 我们将通过列表表达式对其进行拆解,
# 将多维列表中的所有元素抽离出来形成单个列表
result = [item for items in target for item in items]
print(result)
执行效果
[0, 1, 2, 3, 4, 5, 6, 7, 8]
注:
[item for items in target for item in items]
可以理解为如下代码:
for items in target:
for item in items:
return item
用于承接从序列中取出的元素的循环变量将被定义在其所处的环境中,这意味着循环变量可能会修改已定义的同名变量的值。对此,请参考如下示例:
constant = 1
for constant in range(101):
pass
print(constant)
执行结果
在循环过程中,循环变量的改变会导致已有的同名变量发生改变,导致变量被污染。
100
使用列表推导式可以避免循环变量被污染的现象发生。对此,请参考如下示例:
constant = 1
result = [constant for constant in range(101)]
print(constant)
执行效果
1
集合推导式与列表推导式十分类似,不同的是,列表推导式的执行将创建一个列表,而集合推导式的执行将创建一个集合。对此,请参考如下示例:
# 向集合中重复添加相同的元素 1
result = {1 for i in range(100)}
print(result)
print(type(result))
执行结果
{1}
<class 'set'>
使用字典表达式,你将能够快速生成一个字典。对此,请参考如下示例:
content = 'Hello World'
result = {content[i] : i for i in range(len(content))}
print(result)
执行结果
{'H': 0, 'e': 1, 'l': 9, 'o': 7, ' ': 5, 'W': 6, 'r': 8, 'd': 10}
在编程语言理论中,惰性求值(英语:Lazy Evaluation),又译为惰性计算、懒惰求值,也称为传需求调用(call-by-need),是一个计算机编程中的一个概念,目的是要最小化计算机要做的工作。惰性计算的最重要的好处是它可以在空间复杂度上得到极大的优化,从而可以轻易构造一个无限大的数据类型。
惰性求值的相反是及早求值,这是一个大多数编程语言,如C语言,所使用的缺省计算方式。
上述内容引用自 维基百科
采用惰性求值的方式时,一个值仅在该值被需要时才进行计算。假设我们以惰性求值的方式创建了一个列表,在对该列表进行遍历循环时,仅在循环遍历到索引为 X 的元素时才会生成该元素,并且此时位于 X 后的元素都并未生成,而位于 X 之前的元素及索引 X 所对应的元素都将在其下一个值被计算时被去除。因此,采用惰性求值时,仅能向后计算,若需要获得已计算的值,则需要重新开始计算(重新对列表进行遍历循环)。
惰性求值所能够带来的优势
采用惰性求值的方式创建列表,将能够突破计算机的容量(内存)限制,创建一个包含无穷多的元素的列表。
采用惰性求值方式,我们可以不必在短时间内产生大量的计算结果。由于没有这些计算结果占据内存空间,程序的性能将得到一定的提升(对于遇到内存容量瓶颈的程序)。
Python 中的生成器采用惰性求值的方式来产生元素。列表推导式与生成器推导式十分类似,只是生成器推导式的外侧使用的是小括号 () ,而列表推导式的外侧使用的是中括号 []。使用生成器推导式,我们将能够产生一个可迭代出指定元素合集的生成器。对此,请参考如下示例:
result = (i for i in range(101) if i % 2 == 0)
print(result)
for j in result:
print(j, end='\t')
执行效果
<generator object <genexpr> at 0x7fd3cede03c0>
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94 96 98 100
注:
当生成器推导式作为函数的唯一参数时,位于生成器表达式外侧的括号可被省略。对此,请参考如下示例:
arr = list(i for i in range(101) if i % 2 == 0)
print(arr)
执行效果
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100]