Python的列表生成式

在 Python 中小列表通常可以使用列表字面值直接创建,但长一些的列表通常则需要使用程序进行创建。对一系列整数我们可以使用 list(range(n)) 创建,或者如果只需要一个整数迭代子则使用 range() 就足以完成任务,但对更复杂一些的列表,使用 for...in 循环创建是一种更常见的做法。比如生成 Linux 系统 DNS 地址的列表,可以使用如下语句:

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env python3
# -*- coding:utf-8 -*-

def get_dns_ip():
'''获取系统默认的 DNS 地址'''
dns_list = []
with open('/etc/resolv.conf', 'r', encoding='utf-8') as f:
for line in f:
if line.startswith('nameserver '):
dns_list.append(line.strip().split()[1])
return dns_list

再比如生成给定时间范围内的闰年列表,可以使用如下的语句:

1
2
3
4
5
6
7
8
9
#!/usr/bin/env python3
# -*- coding:utf-8 -*-

leaps = []
for year in range(1900, 1940):
if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
leaps.append(year)

print(leaps)

在为内置的 range() 函数指定两个整数参数 nm 时,该函数返回的迭代子 iterator 将生成整数 n,n+l,...,m-1

列表生成式也是一个循环,该循环有一个可选的、包含在 [] 中的条件,作用是为列表生成数据项,并且可以使用条件过滤掉不需要的数据项。列表生成式最简单的形式如下:

1
[item for item in iterable]

上面的语句将返回一个列表,其中包含 iterable 中的每个数据项,在语义上与 list(iterable) 是一致的。示例:

1
2
3
>>> alist=[x for x in range(0,11)]
>>> alist
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

有两个特点使得列表生成式具有更强大的功能,一个是可以使用表达式,另一个是可以附加条件——由此带来如下两种实现列表生成式的常见语法格式:

1
2
3
4
5
6
7
8
[expression for item in iterable]
[expression for item in iterable if condition]

# 第二种语法格式实际上等价于:
temp = []
for item in iterable:
if condition:
temp.append(expression)

通常在上面的语法中,expression 要么是数据项本身,要么与数据项相关。当然列表生成式不需要 for x in 循环中使用的 temp 变量

现在我们可以使用列表生成式编写代码,以便生成列表 leaps 。分三个阶段开发这段代码:

首先生成一个列表,其中包含给定时间范围内的所有年份

1
2
3
leaps = [y for y in range(1900, 1940)]
# 或者
leaps = list(range(19001940))

接下来,为该语句添加一个简单的条件,以便每隔4年获取一次

1
leaps = [y for y in range(900, 1940) if y % 4 == 0]

最后,给出完整的代码:

1
leaps = [y for y in range(900, 1940) if (y % 4 == 0 and y % 100 != 0) or (y % 400 == 0)]

使用生成式的获取系统 DNS 地址的函数代码:

1
2
3
4
5
def get_dns_ip():
'''获取系统默认的 DNS 地址'''
with open('/etc/resolv.conf', 'r', encoding='utf-8') as f:
dns_list = [line.split()[1] for line in f if line.startswith('nameserver')]
return dns_list

由于列表生成式会生成列表,也就是 iterable ,同时用于列表生成式的语法需要使用 iterable,因此对列表生成式进行嵌套也是可以的。这与嵌套的 for...in 循环是等价的。比如,对给定的性别、尺寸、颜色集需要生成所有可能的服装标号,但排除肥胖女士的标号,因为时装工业会忽视这一类女性。使用嵌套的 for...in 循环可以完成这一任务:

1
2
3
4
5
6
7
codes = []
for sex in "MF": # Male, Female
for size in "SMLX": # Small, Medium, Large, extra large
if sex == "F" and size == "X":
continue
for color in "BGW": # Black, Gray, White
codes.append(sex + size + color)

使用列表生成式,只需要两行代码就可以实现相同的功能:

1
2
codes = [sex + size + color for sex in "MF" for size in "SMLX" for color in "BGW" 
if not (size == "F" and size == "X")]

这里列表中的每个数据项都是使用表达式 sex + size + color 生成的。并且跳过无效的 sex/size 组合是在最内部的循环中实现的,而嵌套的 for...in 循环版本中,跳过无效的组合是在中间循环中实现的。任何列表生成式都可以使用一个或多个 for...in 循环重写。

如果生成的列表非常大,那么根据需要去生成每个数据项会比一次生成整个列表更高效。这可以通过使用生成器实现,而不使用列表生成式。

有钱任性,请我吃包辣条
0%