2.4 高效的推导式

前面我们详细介绍了for循环的操作细节。其实,还有一个“精简”版的for循环,称为推导式(comprehensions,又称解析式),它是Python中的一种独有特性。它能够非常简洁地按照某种规则,以一个数据序列(如列表、字典和集合等)为基础,推导出另一个新的数据序列

推导式共有三种类型,分别是列表推导式、字典推导式和集合推导式。下面我们分别给予简单介绍。

2.4.1 列表推导式

列表推导式的语法形式非常简单,如下所示。

最外层的方括号是列表的标志性身份,它表明这个表达式的结果是生成一个列表,故称列表推导式(list comprehensions)。在功能上,方括号内描述的列表推导式相当于一个循环,只不过形式更加简洁罢了。图2-14给出了代码说明。

图2-14 基于for循环的列表推导式

下面,我们再用几个示例来说明列表推导式的强大功能。

1.过滤原始序列中不符合条件的元素

在列表推导式中,我们可以通过if语句的逻辑判断,筛选符合条件的元素。例如,如果我们想把一个列表中的整数提取出来,并做平方处理,可以通过如图2-15所示的方法来实现。

图2-15 通过列表推导式筛选元素

2.使用列表推导式实现嵌套列表的平铺

在前面的列表推导式例子里,我们仅使用一层for循环来产生新的列表。事实上,我们也可以使用两层for循环。下面的代码就是利用两层for循环将嵌套列表平铺成一个列表的示例。

这个列表推导式中包括两个for循环,其中,第一个for循环可视为外循环,第二个循环可视为内循环。外循环每读取一个元素(某个内部列表元素,如[1,2,3]),内循环要遍历列表元素中的三个子元素(如1、2、3)。很显然,外循环跑得慢,而内循环跑得快。方括号最前方的那个num,就是所谓的输出表达式,虽然它看起来就是一个变量模样。

3.多条件组合构造特定列表

如前所述,列表推导式包含一对括号,在括号中有一个输出表达式,表达式后面紧跟一条for语句,然后是0条或多条for语句、if语句,通过各种组合,能够构造出各类高阶列表。例如,下面的列表推导式将两个不同列表中的元素整合到了一起。

需要注意的是,如果表达式是一个元组,如In [12]处的(x,y),那么必须得给它加上括号。

通过前面的讲解,我们知道,在In [12]处的一条语句,相当于两个嵌套的for循环,内循环中添加了一个if语句,与其等价的常规for循环代码如下所示。

对比上述两段功能相同的程序,我们要注意两点:第一,在列表推导式所表征的代码中,for和if等逻辑控制关键词出现的顺序,与常规代码中for和if等关键词出现的顺序是相同的;第二,列表推导式的代码是简单的,但等价的常规代码可读性更强,具体哪种更好,仁者见仁,智者见智。

2.4.2 字典推导式

字典推导式和列表推导式的使用方法比较类似,不过是把列表的标志——一对方括号[],变更为字典的标记——一对花括号{}。举例说明,下面代码的功能是交换原有字典的键和值。

上述代码的In [2]处使用了字典的items()方法,它会返回一个支持遍历操作的列表,列表中是诸如(键0,值0)、(键1,值2)这样的小元组。

2.4.3 集合推导式

集合推导式和字典推导式非常类似,它们都有一个核心标志——一对花括号。但有所不同的是,字典内的元素需要以“键/值对”的形式出现,这里冒号为“键”和“值”的分隔符。而集合则不需要这个冒号,且集合内的元素是不能重复的。换句话说,集合推导式和字典推导式的差别,就是集合和字典之间的差别。示例代码如下。

在上述代码的In [1]处,由于1和-1的平方都是1,2和-2的平方都是4,它们都是集合的元素,而集合的眼中容不下相同的元素,所以两个1和两个4,都分别保留了一个。经过这个操作,集合推导式实际实现了元素去重的效果。