前面我们已经学过列表,也写过很多 for 循环。比如遍历数据、筛选数据、批量处理数据,这些你其实已经做过很多次了。到了这一章,Python 会给你一个非常有代表性的高级写法:列表推导式。
很多人第一次看到列表推导式,会有两种反应。
第一种反应是,哇,这一行怎么能写这么多。 第二种反应是,这种写法是不是只是为了炫技。
其实都不对。
列表推导式真正的价值,不是让代码显得厉害,而是让一些非常常见的列表生成逻辑,写得更紧凑、更清楚、更像 Python 风格。
也正因为它太有代表性了,所以很多人会把它当成“Pythonic 写法”的起点。
这一章,我们就把它彻底讲清。
一、先别急着背语法,先看你以前是怎么写的
比如你现在有一个列表:
nums = [1, 2, 3, 4, 5]
你想把里面每个数字都变成平方数,最自然的写法通常是这样:
nums = [1, 2, 3, 4, 5]result = []for n in nums: result.append(n * n)print(result)
输出结果:
[1, 4, 9, 16, 25]
这段代码当然完全没问题。 而且对初学者来说,这种写法非常重要,因为它最直白。
但你会发现,这种逻辑其实特别常见:
准备一个空列表 遍历原数据 把处理后的结果 append 进去
当这种模式反复出现时,Python 就给你提供了一个更紧凑的表达方式,这就是列表推导式。
二、列表推导式到底是什么
先给一个最直白的定义:
列表推导式,就是用一行更紧凑的写法,直接生成一个新列表。
刚才那段代码,用列表推导式写出来就是:
nums = [1, 2, 3, 4, 5]result = [n * n for n in nums]print(result)
输出结果还是:
[1, 4, 9, 16, 25]
你会发现,效果完全一样。 但写法明显更集中。
所以你可以先把列表推导式理解成:
把原来那种“循环加 append”的固定模式,压缩成一种更简洁的列表生成语法。
三、先把这个结构看懂
刚才这句是本章最核心的起点:
[n * n for n in nums]
你先别急着一口气背,先按顺序看。
最左边:
n * n
表示你想放进新列表里的元素长什么样。
后面这段:
for n in nums
表示数据从哪里来,以及怎么遍历。
所以整句话翻译成人话就是:
从 nums 里一个一个取出 n把每个 n * n 放进新列表 最终生成整个结果列表
这样一翻译,是不是就顺多了。
四、列表推导式和普通 for 循环的关系
这一点特别重要。
很多新手看到推导式后,会觉得自己是不是以后都不需要 for 循环了。不是。
列表推导式不是替代 for 循环,而是在一种很典型的场景下,给你一个更适合的写法。
这个场景就是:
我要从一组旧数据,快速生成一组新列表。
比如:
每个元素做一次处理 把结果收集成新列表 或者先筛选,再收集成新列表
如果只是单纯做遍历、打印、复杂逻辑分支,那很多时候普通 for 循环依然更自然。
所以你一定不要有一个误区:
列表推导式不是为了取代所有循环。 它只是更适合“生成新列表”这个场景。
五、最基础的模板长什么样
你现在先记住这个最基础模板:
[表达式 for 变量 in 可迭代对象]
比如:
[x * 2for x in [1, 2, 3]]
意思就是:
遍历 [1, 2, 3]每次取出 x把 x * 2 的结果放进新列表
输出:
[2, 4, 6]
这个模板你后面会反复用到。 它就是所有列表推导式的骨架。
六、为什么说它是 Pythonic 写法的开始
因为 Python 这门语言有一个很鲜明的特点:
对于高频模式,它往往会提供更简洁、更优雅的表达方式。
而“遍历一个序列,把处理结果收集成列表”这件事,实在太高频了。 高频到什么程度?
文本处理会用到 数据清洗会用到 文件处理会用到 爬虫会用到 自动化脚本会用到 几乎所有项目都可能会用到
所以列表推导式就变成了 Python 里特别有代表性的一种风格。
不是因为它花哨,而是因为它太常用了。
七、再看几个特别典型的例子
例子 1,把所有字符串都变成大写:
words = ['python', 'java', 'go']result = [word.upper() for word in words]print(result)
输出:
['PYTHON', 'JAVA', 'GO']
例子 2,把字符串列表转成长度列表:
words = ['apple', 'banana', 'cat']result = [len(word) for word in words]print(result)
输出:
[5, 6, 3]
例子 3,把数字全部转成字符串:
nums = [10, 20, 30]result = [str(n) for n in nums]print(result)
输出:
['10', '20', '30']
你会发现,这些都属于同一种模式:
拿旧列表 逐个处理 生成新列表
这就是列表推导式最擅长的事情。
八、列表推导式不只是处理,还可以筛选
这一步特别重要。
除了“遍历并加工”,列表推导式还特别适合“边筛选边生成”。
比如你现在只想保留偶数:
普通写法:
nums = [1, 2, 3, 4, 5, 6]result = []for n in nums:if n % 2 == 0: result.append(n)print(result)
输出:
[2, 4, 6]
列表推导式写法:
nums = [1, 2, 3, 4, 5, 6]result = [n for n in nums if n % 2 == 0]print(result)
输出还是:
[2, 4, 6]
这就说明,列表推导式不只是能处理元素,也能附带筛选条件。
九、这时候模板要升级一下
当你需要筛选时,模板会变成:
[表达式 for 变量 in 可迭代对象 if 条件]
比如:
[x for x in nums if x > 10]
这句话翻译成人话就是:
遍历 nums只保留大于 10 的那些 x把它们放进新列表
你会发现,这种写法在数据处理里特别自然。
十、再看几个筛选类例子
例子 1,找出长度大于 3 的单词:
words = ['hi', 'python', 'cat', 'banana']result = [word for word in words if len(word) > 3]print(result)
输出:
['python', 'banana']
例子 2,找出所有正数:
nums = [-3, -1, 0, 2, 5, -7, 8]result = [n for n in nums if n > 0]print(result)
输出:
[2, 5, 8]
例子 3,筛出所有包含字母 a 的单词:
words = ['apple', 'dog', 'banana', 'cat']result = [word for word in words if'a'in word]print(result)
输出:
['apple', 'banana', 'cat']
这些例子会帮助你建立一个非常关键的感觉:
列表推导式特别适合做“筛选加收集”。
十一、处理和筛选还能放在一起
这一点你会很常用。
比如你想从一组数字里,只拿偶数,并且把这些偶数变成平方数。
普通写法:
nums = [1, 2, 3, 4, 5, 6]result = []for n in nums:if n % 2 == 0: result.append(n * n)print(result)
列表推导式写法:
nums = [1, 2, 3, 4, 5, 6]result = [n * n for n in nums if n % 2 == 0]print(result)
输出:
[4, 16, 36]
你会发现,这种写法一旦读顺了,会非常舒服。
它本质上表达的是:
从 nums 里遍历 只取偶数 把每个偶数平方 收集成新列表
这一类逻辑,列表推导式几乎天生就适合。
十二、为什么很多人刚开始会觉得它难
因为它把原来分成好几行的逻辑,压到了一行里。 信息密度一下变高了。
以前你是按这种顺序写:
先写空列表 再写循环 再写条件 再写 append
而列表推导式把这些东西挤到一个表达式里后,大脑需要适应新的阅读顺序。
所以你一开始觉得别扭,非常正常。 关键不是马上追求写得飞快,而是先学会“拆着看”。
比如这句:
[n * n for n in nums if n % 2 == 0]
你可以拆成三部分理解:
结果长什么样 数据从哪来 条件是什么
一旦学会这么拆,它就不再难了。
十三、最容易犯的一个错:把推导式写得太复杂
这是必须提前提醒你的。
列表推导式很好用,但不是说越长越高级。 如果你往里面塞太多层逻辑、太多条件、太多判断,它很快就会从“简洁”变成“难读”。
比如这种简单场景:
[x * 2for x in nums]
很清楚。
这种也还行:
[x * 2for x in nums if x > 0]
但如果你开始把很多复杂分支都压进去,代码就会变得难读。 那时候,与其硬追求一行,不如老老实实写普通循环。
所以要记住一个特别实用的原则:
列表推导式的前提是更清楚,不是更炫。
十四、什么时候特别适合用列表推导式
你可以先记住下面几类高频场景:
把旧列表加工成新列表 对每个元素做同一种处理 边筛选边生成新列表 从字符串、数字、对象中提取某个属性或结果 想让“生成新列表”这件事写得更紧凑
比如:
批量去空格 批量转大写 批量取长度 批量转类型 筛出符合条件的数据
这些几乎都是它的天然主场。
十五、什么时候不太适合硬用
如果你遇到下面这些情况,就要谨慎一点:
逻辑分支特别多 条件嵌套特别深 要做的不只是生成列表 中间还要打印、记录、调试 代码写成一行后自己都要看半天
这时候普通 for 循环往往更合适。
所以你千万不要把列表推导式理解成“看到循环就一定要改成推导式”。 不是这样。
真正成熟的写法永远是:
选更合适的表达方式,而不是盲目追求更短。
十六、做一个特别贴近数据处理的小案例
假设你从文件里读出来一批原始文本,其中有很多空格。 你想把每一行都清洗一下。
lines = [' python ', ' java', 'go ', ' c++ ']cleaned = [line.strip() for line in lines]print(cleaned)
输出:
['python', 'java', 'go', 'c++']
这个例子特别有代表性。 因为你会发现,真实数据处理里这种“批量清洗”场景太常见了。
如果不用列表推导式,当然也能写。 但推导式在这里真的会显得特别顺手。
十七、再看一个更像业务代码的例子
假设你有一批学生成绩,想找出及格成绩,并统一加上一个文本说明。
scores = [58, 76, 90, 43, 67, 81]result = [f'成绩:{score}'for score in scores if score >= 60]print(result)
输出:
['成绩:76', '成绩:90', '成绩:67', '成绩:81']
这时候你应该开始真正体会到:
列表推导式不是玩具。 它就是数据处理里很顺手的一种表达工具。
十八、列表推导式和你前面学过的内容,其实是连着的
这一章不是凭空冒出来的。
你前面学了:
列表 循环 条件判断 字符串处理 文件读写 模块和标准库
列表推导式其实就是建立在这些基础之上的更紧凑写法。
所以如果你前面循环、条件、列表处理还不太稳,这一章就会觉得有点快。 这不是因为它多神秘,而是因为它本身就是在压缩前面已经学过的逻辑。
你也可以反过来理解:
列表推导式,是对前面基础能力的一次浓缩表达。
这也是为什么它会成为“高级语法与进阶特性”阶段的第一章。
十九、本章小练习
你可以做三个特别适合巩固的练习。
练习 1 把下面列表中的每个数字都加 10:
nums = [1, 2, 3, 4, 5]
练习 2 从下面列表中筛出所有奇数:
nums = [1, 2, 3, 4, 5, 6, 7, 8]
练习 3 把下面字符串列表里长度大于 4 的单词全部转成大写:
words = ['cat', 'python', 'book', 'banana', 'go']
参考答案:
nums = [1, 2, 3, 4, 5]result1 = [n + 10for n in nums]print(result1)nums = [1, 2, 3, 4, 5, 6, 7, 8]result2 = [n for n in nums if n % 2 == 1]print(result2)words = ['cat', 'python', 'book', 'banana', 'go']result3 = [word.upper() for word in words if len(word) > 4]print(result3)
只要你把这三个练习亲手写一遍,这一章的主干就会非常稳。
二十、本章总结
这一章最重要的,是把列表推导式的感觉建立起来。
列表推导式,本质上是用更紧凑的方式生成新列表。 它最适合的场景,是遍历旧数据、做处理、再收集成新列表。 基础模板是:
[表达式 for 变量 in 可迭代对象]
带筛选条件的模板是:
[表达式 for 变量 in 可迭代对象 if 条件]
它的价值不在于炫技,而在于让一些高频列表生成逻辑写得更清楚、更像 Python 风格。 但前提是代码要更容易读,而不是更难读。复杂逻辑别硬塞进推导式里。
到这里,第十阶段就真正开始了。 下一章我们继续往前走,进入和列表推导式非常自然衔接的内容:092|字典推导式与集合推导式:一行代码处理数据。