python 的 [xxx for a in aaa for b in bbb if ccc] 列表推导式是什么鬼呀!!!

47 阅读4分钟

在 python 中存在一种很奇妙的「列表推导式」语法,例如以下的例子:

res_list = [s.strip() for s in xcol_values.split(',') if s != 'body']

对于这种语法有时候真的很难理解,如何「快速理解」这个语法结构的含义呢?

语法结构

列表推导式的存在意义是定义「从原始数据中筛选并加工数据,生成一个新容器承接数据」的流程。

首先我们先来看看这种语法的基本结构,为后面的快速理解打打基础。

result = [表达式 for 变量 in 可迭代对象 [if 条件]]

他的结构主要是三部分:

  1. 表达式
  2. for 变量 in 可迭代对象
  3. if 条件(被 [] 括起来代表语句可选,可写可写)
  4. 最外层括起来的 []

这种结构对应的实际代码是:

result = []
for 变量 in 可迭代对象:
    if 条件:
        result.append(表达式)

是不是这段实际代码比较好理解,逻辑也清晰一点,没错!列表推导式实际上就是这段代码的简化写法,最终的目的就是实现对这段代码的书写简化,让代码更加简洁专注于业务操作,减少冗余的逻辑代码,说的更精练一些就是一种语法糖。

快速理解

快速理解分为四步:

第一步:确定数据源

以刚刚上面的代码作为例子:

res_list = [s.strip() for s in xcol_values.split(',') if s != 'body']

在代码中 for 变量 in 可迭代对象 的结构就是数据源(看到 for 和 in 就整块圈出来理解),我们需要先确认数据是从什么地方读取出来的,可以看到 xcol_values.split(',') 代表字符串 xcol_values 使用 , 分割后的字符串数组;

那么 for s in xcol_values.split(',') 就是代表从字符串 xcol_values 使用 , 分割后的字符串数组中遍历每一个字符串元素,每次遍历时的字符串元素使用 s 变量存储。

这个时候我们就确定数据源,也就是要处理的是那一些数据,后续准备筛选和加工的的就是这些数据啦。

第二步:确认筛选条件

for 变量 in 可迭代对象 后面跟着的 if 开头的 if 条件 就是定义的筛选条件,他表示对数据源中的每一个遍历读取的元素,在这里也就是 s进行 if 条件 语句的判断,如果是 true 则代表 s 变量中的元素可以留下,否则是 false 代表这个元素必须丢弃。

例子中的 if s != 'body' 表示对字符串数组中的每一个字符串元素 s,如果字符串不是 'body' 就可以留下,是 'body' 就丢弃不要,实际作用就是筛选出数据源中不是 'body' 的字符串元素留下。

这个筛选条件是可选的,也就是 for 变量 in 可迭代对象 后面可以不跟 if 条件,表示从数据源中读取的元素不进行任何的筛选操作。

第三步:确认加工流程

经过前两步的「数据源」和「筛选」数据已经是初见端倪了,这时候是可以直接使用容器装了吗,其实在装入容器前还可以再进行一下加工,也就是位于列表推导式最开始的语句 表达式 所定义的操作。

s.strip() 代表对筛选后留下的字符串元素,去掉其周围空格。

第四步:确认装入容器

最后经过了从什么「数据源」遍历读取出数据,然后对数据进行逐个「筛选」最后选剩下的数据逐个「加工」就可以放入生成的新容器中了,这个容器要确认一下是「列表」、「集合」还是「字典」等,因为不同的容器存储数据的特点是不一样的,例如集合是无序不重复的,列表是有序可重复的,字典是需要有 key 和 value 的,

不同的特点最后可能还会进行操作,举个例子集合就会对最后加工出来的元素在进行一次自动的去重,而列表就不会自动去重,但是会天然的保持元素从数据源出来到推导式生成元素的顺序。

[s.strip() for s in xcol_values.split(',') if s != 'body'] 是使用 [] 括起来的代表生成容器就是列表,最后所有数据都将用列表承接装入;

使用 () 括起来是元组,对应他的推导式叫做元组推导式;

使用 {} 括起来的,如果表达式生成的是单个元素,那么是集合,对应叫做集合推导式,如果表达式生成的是键值对,那么是字典,对应叫做字典推导式。

# 集合推导式
{key 表达式: value 表达式 for 变量 in 可迭代对象 [if 条件]}

# 字典推导式
{表达式 for 变量 in 可迭代对象 [if 条件]}

总结:

一个列表推导式,按照上面这四步依次拆分出每部分然后理解,那么理解代码流程的速度会快很多,理解起来也会轻松很多,有章法可循。

graph LR
START --> 1.确定数据源 --> 2.确认筛选条件 --> 3.确认加工流程 --> 4.确认装入容器 --> STOP