流畅的 Python(13):序列构成的数组 2.2&2.2.1

阅读数:13 2019 年 11 月 20 日 16:56

流畅的Python(13):序列构成的数组 2.2&2.2.1

内容简介
本书致力于帮助 Python 开发人员挖掘这门语言及相关程序库的优秀特性,避免重复劳动,同时写出简洁、流畅、易读、易维护,并且具有地道 Python 风格的代码。本书尤其深入探讨了 Python 语言的高级用法,涵盖数据结构、Python 风格的对象、并行与并发,以及元编程等不同的方面。

(列表推导和生成器表达式)

列表推导是构建列表(list)的快捷方式,而生成器表达式则可以用来创建其他任何类型的序列。如果你的代码里并不经常使用它们,那么很可能你错过了许多写出可读性更好且更高效的代码的机会。

如果你对我说的“更具可读性”持怀疑态度的话,别急着下结论,我马上就能说服你。

很多 Python 程序员都把列表推导(list comprehension)简称为 listcomps,生成式表达器(generator expression)则称为 genexps。我有时也会这么用。


(列表推导和可读性)

先来个小测试,你觉得示例 2-1 和示例 2-2 中的代码,哪个更容易读懂?

示例 2-1 把一个字符串变成 Unicode 码位的列表

>>> symbols = '$¢£¥€¤'
>>> codes = []
>>> for symbol in symbols:
... codes.append(ord(symbol))
...
>>> codes
[36, 162, 163, 165, 8364, 164]

示例 2-2 把字符串变成 Unicode 码位的另外一种写法

>>> symbols = '$¢£¥€¤'
>>> codes = [ord(symbol) for symbol in symbols]
>>> codes
[36, 162, 163, 165, 8364, 164]

虽说任何学过一点 Python 的人应该都能看懂示例 2-1,但是我觉得如果学会了列表推导的话,示例 2-2 读起来更方便,因为这段代码的功能从字面上就能轻松地看出来。

for 循环可以胜任很多任务:遍历一个序列以求得总数或挑出某个特定的元素、用来计算总和或是平均数,还有其他任何你想做的事情。在示例 2-1 的代码里,它被用来新建一个列表。

另一方面,列表推导也可能被滥用。以前看到过有的 Python 代码用列表推导来重复获取一个函数的副作用。通常的原则是,只用列表推导来创建新的列表,并且尽量保持简短。如果列表推导的代码超过了两行,你可能就要考虑是不是得用 for 循环重写了。就跟写文章一样,并没有什么硬性的规则,这个度得你自己把握。

句法提示
Python 会忽略代码里 []{}() 中的换行,因此如果你的代码里有多行的列表、列表推导、生成器表达式、字典这一类的,可以省略不太好看的续行符 \

列表推导不会再有变量泄漏的问题
Python 2.x 中,在列表推导中 for 关键词之后的赋值操作可能会影响列表推导上下文中的同名变量。像下面这个 Python 2.7 控制台对话:

Python 2.7.6 (default, Mar 22 2014, 22:59:38)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> x = 'my precious'
>>> dummy = [x for x in 'ABC']
>>> x
'C'

如你所见,x 原本的值被取代了,但是这种情况在 Python 3 中是不会出现的。
列表推导、生成器表达式,以及同它们很相似的集合(set)推导和字典(dict)推导,在 Python 3 中都有了自己的局部作用域,就像函数似的。表达式内部的变量和赋值只在局部起作用,表达式的上下文里的同名变量还可以被正常引用,局部变量并不会影响到它们。
这是 Python 3 代码:

>>> x = 'ABC'
>>> dummy = [ord(x) for x in x]
>>> x ➊
'ABC'
>>> dummy ➋
[65, 66, 67]
>>>

x 的值被保留了。
➋ 列表推导也创建了正确的列表。

列表推导可以帮助我们把一个序列或是其他可迭代类型中的元素过滤或是加工,然后再新建一个列表。Python 内置的 filtermap 函数组合起来也能达到这一效果,但是可读性上打了不小的折扣。

图灵地址 https://www.ituring.com.cn/book/1564

评论

发布