首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >开源图书《Python完全自学教程》6.4解析

开源图书《Python完全自学教程》6.4解析

作者头像
老齐
发布2022-07-06 15:53:29
发布2022-07-06 15:53:29
1.3K0
举报
文章被收录于专栏:老齐教室老齐教室

6.4 解析

解析(Comprehension,或翻译为“推导”)是一种典型的 Python 语法,可以在列表、字典、元组、集合上使用(注意,Python 2 只有列表解析),解析式可以针对任何可迭代对象,通过一行代码,完成循环语句中多行代码的任务。

6.4.1 列表解析

创建列表并在其中添加某些成员,这是一种比较常见的操作,比如创建一个列表,其中的成员要求是1~9的整数的平方,用6.3节中学过的 for 循环,可以这样实现:

代码语言:javascript
复制
>>> lst = []
>>> for i in range(1, 10):
...     lst.append(i * i)
...
>>> lst
[1, 4, 9, 16, 25, 36, 49, 64, 81]

如果用本节所要介绍的列表解析,就变成了:

代码语言:javascript
复制
>>> [i * i for i in range(1, 10)]   # (1)
[1, 4, 9, 16, 25, 36, 49, 64, 81]

对比两部分代码,后者更简洁,此外,后者运行时间还更短。对此,可以写个程序检验一下。

代码语言:javascript
复制
#coding:utf-8
'''
testcomprehension.py
'''
import time

start = time.time()
lst = [i * i for i in range(100000)]
end = time.time()
delta = end - start

print(f"list comprehension time: {delta}")

start2 = time.time()
lst2 = []
for i in range(100000):
    lst.append(i * i)
end2 = time.time()
delta2 = end2 - start2

print(f"for loop time: {delta2}")
print(f"(list comprehension) / (for loop) = {delta/delta2:.3f}")

执行程序,结果如下:

代码语言:javascript
复制
% python testcomprehension.py
list comprehension time: 0.01116490364074707
for loop time: 0.02745819091796875
(list comprehension) / (for loop) = 0.407

不同的机器,上述结果会有所差异。在我的机器上,如果要生成一个由 100000以内的正整数的平方为成员的列表,使用列表解析要比使用 for 循环速度快,而且还快不少。

所以,非常有必要认真学习这个强大的工具。

以注释(1)为例,列表解析的基本结构是:

代码语言:javascript
复制
[expression for member in iterable]

其中的“expression”意为表达式,不仅仅可以是注释(1)中所示的数学算式,还可以有其他形式,比如:

代码语言:javascript
复制
>>> str_int = [str(i) for i in range(10)]
>>> str_int
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
>>> [i for i in '521']
['5', '2', '1']

此外,也可以在列表解析中增加条件,语法结构为:

代码语言:javascript
复制
[expression for member in iterable (if conditional)]

例如,选出列表 ['anwang', 'microsoft', 'ibm', 'compaq', 'lenovo', 'dell'] 中含有字母 'a' 的单词:

代码语言:javascript
复制
>>> lst = ['anwang', 'microsoft', 'ibm', 'compaq', 'lenovo', 'dell']
>>> [word for word in lst if 'a' in word]    # (2)
['anwang', 'compaq']

特别注意,注释(2)中的列表解析,将条件判断放在了最后的位置,不要放在 for 关键词之前。

读者可以用此方法重写此前 for 循环中很多程序,比如6.3.1节的 divisibility.py ,求 50以内能被 3 整除的非负整数:

代码语言:javascript
复制
lst = [i for i in range(50) if i % 3 == 0]
print(f"numbers divisible by 3: {lst}")

以上介绍的是列表解析的基本形式,在此基础上,还可以有很多变化,比如嵌套列表解析。

代码语言:javascript
复制
>>> matrix = [
... [1,2,3,4],
... [5,6,7,8],
... [9,10,11,12]]
>>> matrix
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]

变量 matrix 应用了一个嵌套列表,我们可以把它看做一个 3\times4 的矩阵,如果用已学过的知识,将这个矩阵转置,即实现:

\begin{bmatrix}1&2&3&4\\5&6&7&8\\9&10&11&12\end{bmatrix}^{\rm{T}}=\begin{bmatrix}1&5&9\\2&6&10\\3&7&11\\4&8&12\end{bmatrix}

对此类操作都有专用工具(如 Numpy ,参阅第12章12.4节),在真实的工程项目中,也一定要用专用工具。下面的代码仅仅是为了演示如何实现嵌套列表解析,故称之为“玩具代码”,勿要搬用到实际工作中。

代码语言:javascript
复制
>>> [[row[i] for row in matrix] for i in range(4)]    # (3)
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

注释(3)的列表解析有两层,for row in matrix 循环 matrix 的列表成员,相当于矩阵的行;for i in range(4) 依次得到每行——是一个列表——的成员索引。

同样的问题,常常有多个解决方法——真实世界的问题不是只有一个答案。

代码语言:javascript
复制
>>> list(zip(*matrix))
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

请读者结合6.3.2节中学过的 zip() 函数理解上述操作——又是“温故而知新”。

再看这样一个问题,更要读者熟练掌握已学知识。

代码语言:javascript
复制
>>> gender = ['male', 'female', 'male', 'female', 'female', 'female']
>>> [0 if gen == 'female' else 1 for gen in gender]    # (4)
[1, 0, 1, 0, 0, 0]

列表 gender'male''female' 组成,若将这类数据用于机器学习,必须转化为数字。通常用 0 表示 'female'1 表示 'male' 。注释(4)的列表解析就是要实现此目的。可能觉得它有点违背前述列表解析的结构,为什么 if 放在了 for 的前面?请务必仔细观察,在注释(4)中,“ 0 if gen == 'female' else 1 ”是一个单元,即6.2节中学过的“三元操作”,本质上它是一个表达式,对应着列表解析结构中的 expression 。

6.4.2 字典和集合解析

除了列表解析之外,字典、集合也能应用解析的形式,例如:

代码语言:javascript
复制
>>> lst = [random.randint(1, 10) for _ in range(100)]
>>> lst
[10, 4, 4, 1, 2, 5, 2, 2, 4, 3, 10, 4, 5, 6, 7, 6, 3, 4, 1, 6, 2, 9, 1, 5, 4, 4, 5, 6, 2, 10, 1, 9, 8, 5, 6, 7, 1, 1, 3, 10, 9, 4, 2, 5, 5, 9, 4, 7, 7, 8, 2, 6, 6, 8, 8, 10, 3, 9, 2, 3, 10, 1, 3, 5, 5, 5, 4, 6, 8, 5, 2, 4, 5, 7, 9, 6, 5, 4, 4, 7, 2, 5, 8, 2, 6, 2, 10, 2, 5, 4, 8, 8, 2, 4, 7, 1, 4, 8, 10, 6]

用列表解析得到了 100 个从 1~10 中随机选出的整数,其中 for _ in range(100) 表示重复 100 次,因为此过程中用不到循环语句中的变量,所以用下划线 _ 表示——这是习惯写法。下面从列表中挑出大于 5 的数字,并组成一个集合。

代码语言:javascript
复制
>>> {n for n in lst if n > 5}    # (5)
{6, 7, 8, 9, 10}

注释(5)即为集合解析,与列表解析相比,不同之处仅为外面包裹的符号变为了“ { } ”,里面的结构完全一样。

字典解析亦与之类似。以6.3.2节中将 myinfor = {"publish":"phei", "site":"itdiffer.com", "lang":"python"} 的键、值对调练习为例:

代码语言:javascript
复制
>>> myinfor = {"publish":"phei", "site":"itdiffer.com", "lang":"python"}
>>> {v: k for k, v in myinfor.items()}
{'phei': 'publish', 'itdiffer.com': 'site', 'python': 'lang'}

本节开头提到过,元组也可以用于解析,但是,此处不对此给予介绍,留待第9章9.7节学习,因为它还另有深意。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-05-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老齐教室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 6.4 解析
    • 6.4.1 列表解析
    • 6.4.2 字典和集合解析
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档