从零开始学Python 推导式
哈喽~今天我们来啃一啃 Python 里超实用的推导式!它是 Python 独有的简洁语法,核心就是用一行代码替代多行循环+赋值操作,让代码更清爽、执行效率也更高,不管是生成列表、集合还是字典,都能轻松搞定
一、推导式的通用格式
先搞懂通用结构,后面所有推导式都能举一反三
# 通用骨架(中括号/大括号决定生成的容器类型)
新容器 = [表达式 for 变量 in 可迭代对象 if 条件]
逐部分拆解(新手必看)
|
组成部分 |
通俗解释 |
示例 |
|
|
最终生成的结果容器(列表/集合/字典) |
|
|
|
容器标志: |
列表用 |
|
|
最终要存入容器的内容(可以是变量、运算、函数调用) |
|
|
|
遍历的核心逻辑: |
|
|
|
过滤规则:只有满足条件的元素才会被存入容器 |
|
小提醒:推导式本质是语法糖,底层还是循环,只是写法更简洁,新手先理解循环,再看推导式会轻松很多~
二、列表推导式(最常用!先从它入手)
列表是 Python 里最常用的数据结构,列表推导式也是新手最先该掌握的推导式,我们用基础写法 vs 推导式写法的对比,让你一眼看懂区别。
1. 基础版:生成 0~9 的数字列表
先看最基础的循环写法,再看推导式,对比着理解更透彻~
# 方式1:while 循环(最基础的循环逻辑)
my_list1 = [] # 先定义空列表,用来存结果
i = 0 # 定义计数器,从0开始
while i < 10: # 循环条件:i小于10
my_list1.append(i) # 把i添加到列表里
i += 1 # 计数器+1,避免死循环
print("while循环结果:", my_list1) # 输出:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 方式2:for 循环(比while更简洁,新手常用)
my_list2 = []
for i in range(10): # range(10)生成0-9的整数序列,依次赋值给i
my_list2.append(i) # 把i添加到列表
print("for循环结果:", my_list2) # 输出:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 方式3:列表推导式(一行搞定!核心代码)
my_list3 = [i for i in range(10)]
print("列表推导式结果:", my_list3) # 输出:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
逐行解析推导式:
- 最外层
[]:明确告诉 Python,我们要生成一个列表。 - 第一个
i:是表达式,表示“把遍历到的i直接存入列表”。 for i in range(10):遍历逻辑,range(10)生成0到9的整数(包含0、不包含10),每次循环把其中一个数赋值给i。
2. 带过滤条件版:生成 0~9 的偶数列表
如果我们只想要0-9里的偶数,循环写法加个if判断,推导式也能直接加条件~
# for 循环写法
my_list1 = []
for i in range(10):
if i % 2 == 0: # %是取模运算,i%2==0表示i能被2整除(偶数)
my_list1.append(i)
print("for循环偶数列表:", my_list1) # 输出:[0, 2, 4, 6, 8]
# 列表推导式写法(加if条件)
my_list2 = [i for i in range(10) if i % 2 == 0]
print("推导式偶数列表:", my_list2) # 输出:[0, 2, 4, 6, 8]
关键解析:
- 条件
if i % 2 == 0放在遍历逻辑后面,只有满足这个条件的i,才会被存入新列表。 %是取模运算符,比如4%2=0,5%2=1,刚好用来判断奇偶。
3. 嵌套循环版:生成二维列表
比如我们要生成[(1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]这样的二维元组列表,嵌套循环和推导式的写法对比来啦~
# 嵌套for循环写法
my_list1 = []
for i in range(1, 3): # 外循环:i取1、2(range(1,3)包含1、不包含3)
for j in range(3): # 内循环:j取0、1、2
my_list1.append((i, j)) # 把(i,j)元组添加到列表
print("嵌套循环结果:", my_list1)
# 输出:[(1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
# 列表推导式写法(嵌套for)
my_list2 = [(i, j) for i in range(1, 3) for j in range(3)]
print("推导式嵌套结果:", my_list2)
# 输出:[(1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
关键解析:
- 推导式里的
for顺序和嵌套循环完全一致!先写外循环for i in ...,再写内循环for j in ...,千万别搞反顺序。 - 表达式
(i, j)就是我们要存入列表的元组元素。
三、集合推导式
集合(set)的特性是无序、不重复,集合推导式和列表推导式语法几乎一样,只是用{}包裹,还能自动帮我们去重。
1. 基础版:生成 1~5 的集合
# 集合推导式
my_set1 = {i for i in range(1, 6)} # range(1,6)生成1-5
print("集合推导式结果:", my_set1) # 输出:{1, 2, 3, 4, 5}(顺序可能随机,因为集合无序)
print("类型验证:", type(my_set1)) # 输出:<class 'set'>
解析:
- 用
{}包裹,代表生成集合,和列表推导式的[]区分开。 - 结果是集合,所以打印顺序不固定,但元素一定是1-5。
2. 带条件+去重版
# 1. 生成1~10的奇数集合
odd_set = {i for i in range(1, 11) if i % 2 != 0}
print("奇数集合:", odd_set) # 输出:{1, 3, 5, 7, 9}
# 2. 自动去重(超实用!)
duplicate_list = [1, 2, 2, 3, 3, 3, 4] # 有重复元素的列表
unique_set = {x for x in duplicate_list} # 集合推导式自动去重
print("去重后的集合:", unique_set) # 输出:{1, 2, 3, 4}
核心优势:集合推导式天然支持去重,比用循环遍历+判断是否存在更简洁。
四、字典推导式(难点)
字典推导式需要明确定义键(key)和值(value),用key: value的形式,我们结合你之前问的“两个列表合并成字典”,分两种方式讲解(不用zip + 用zip),对比着理解更透彻。
1. 基础版:键值对生成
比如生成“1~5的数字为键,数字的平方为值”的字典:
# 字典推导式
my_dict1 = {i: i ** 2 for i in range(1, 6)}
print("基础字典推导式:", my_dict1)
# 输出:{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
print("类型验证:", type(my_dict1)) # 输出:<class 'dict'>
解析:
- 字典推导式必须用
{key: value}的形式,key是键,value是值,中间用:分隔。 i: i**2表示“键是遍历的i,值是i的平方”。
2. 两个列表合并成字典(两种方式对比)
假设我们有两个列表:
names_list = ['乔峰', '虚竹', '段誉'] # 名字列表(键)
ages_list = [38, 28, 18] # 年龄列表(值)
我们要把它们合并成{'乔峰': 38, '虚竹': 28, '段誉': 18}的字典,
基础版本
names_list = ['乔峰', '虚竹', '段誉']
ages_list = [38, 28, 18]
# 分步操作
combination_dict = {}
i = 0
for key in names_list:
combination_dict[key] = ages_list[i]
i += 1
print(combination_dict)
推导式分两种写法:
方式1:不用 zip(基于索引)
# 字典推导式(用索引关联两个列表)
name_age_dict = {names_list[i]: ages_list[i] for i in range(len(names_list))}
print("不用zip的合并结果:", name_age_dict)
# 输出:{'乔峰': 38, '虚竹': 28, '段誉': 18}
逐行解析:
len(names_list):获取名字列表的长度,这里是3,所以range(len(names_list))生成0、1、2三个索引。names_list[i]:取名字列表第i个元素作为字典的键。ages_list[i]:取年龄列表第i个元素作为字典的值。
方式2:用 zip(更简洁)
先先搞懂 zip() 函数本身的作用,再结合推导式使用~
第一步:理解 zip() 函数
zip() 是 Python 内置函数,核心作用是把多个可迭代对象(列表、元组等)按位置“打包”成元组,比如:
# 单独使用 zip() 的示例
zipped = zip(names_list, ages_list)
print("zip打包后的对象:", zipped) # 输出:<zip object at 0x...>(zip对象,可迭代)
print("转成列表看内容:", list(zipped)) # 输出:[('乔峰', 38), ('虚竹', 28), ('段誉', 18)]
解析 zip():
zip(列表1, 列表2)会把两个列表中相同位置的元素配对,生成一个 zip 对象。- 转成列表后能看到,每一个元素都是
(列表1的第i个元素, 列表2的第i个元素)的元组,刚好对应字典的“键-值”。 - 如果两个列表长度不一致,
zip()会以较短的列表为准,只配对到最短的长度。
第二步:zip + 字典推导式
# 字典推导式(用zip合并)
name_age_dict2 = {name: age for name, age in zip(names_list, ages_list)}
print("用zip的合并结果:", name_age_dict2)
# 输出:{'乔峰': 38, '虚竹': 28, '段誉': 18}
逐行解析:
zip(names_list, ages_list):把名字和年龄列表打包成[('乔峰', 38), ('虚竹', 28), ('段誉', 18)]。for name, age in ...:对打包后的每个元组做“解包”,把元组第一个元素赋值给name(作为键),第二个元素赋值给age(作为值)。name: age:用解包后的变量直接生成字典的键值对,比索引方式更简洁。
两种方式对比
|
方式 |
写法特点 |
优点 |
缺点 |
|
不用 zip(索引) |
依赖 |
新手易理解,和基础循环逻辑一致 |
代码稍长,需手动处理索引 |
|
用 zip |
直接打包列表 |
代码更简洁,Python 风格更浓 |
需要先理解 zip 打包/解包 |
3. 进阶:字典键值反转
如果想把现有字典的键和值互换(比如把{'乔峰': 38}变成{38: '乔峰'}),推导式也能轻松搞定:
original_dict = {'乔峰': 38, '虚竹': 28, '段誉': 18}
# 字典推导式反转键值
reversed_dict = {value: key for key, value in original_dict.items()}
print("键值反转后的字典:", reversed_dict)
# 输出:{38: '乔峰', 28: '虚竹', 18: '段誉'}
解析:
original_dict.items():把字典转成可遍历的键值对元组,比如dict_items([('乔峰', 38), ('虚竹', 28), ('段誉', 18)])。value: key:把原来的值当键,原来的键当值,实现反转。
五、生成器表达式(伪“元组推导式”)
新手容易误以为有“元组推导式”,但其实 Python 里没有真正的元组推导式!用()包裹的推导式是“生成器表达式”,它不会一次性生成所有元素,而是迭代时才计算,特别节省内存~
# 生成器表达式(注意:不是元组推导式)
my_generator = (i for i in range(1, 6))
print("生成器对象:", my_generator) # 输出:<generator object <genexpr> at 0x...>
print("类型验证:", type(my_generator)) # 输出:<class 'generator'>
# 遍历生成器(获取里面的元素)
for num in my_generator:
print("生成器元素:", num) # 依次输出 1、2、3、4、5
# 如果想生成元组,需要用 tuple() 转换
my_tuple = tuple(i for i in range(1, 6))
print("生成的元组:", my_tuple) # 输出:(1, 2, 3, 4, 5)
print("类型验证:", type(my_tuple)) # 输出:<class 'tuple'>
核心区别:
- 生成器表达式:
(i for i in ...)→ 生成器对象,按需取值,省内存。 - 元组:
tuple(i for i in ...)→ 把生成器转成元组,一次性生成所有元素。
六、推导式 vs 循环:对比与选择
|
方式 |
代码量 |
可读性 |
性能 |
适用场景 |
|
推导式 |
少(1行) |
高(简洁直观) |
略优 |
简单逻辑、快速生成容器 |
|
for/while 循环 |
多(多行) |
高(步骤清晰) |
稍慢 |
复杂逻辑、多步操作、异常处理 |
选择建议
- ✅ 简单转换/过滤:用推导式(代码更优雅)
- ✅ 复杂业务逻辑:用循环(便于调试和扩展)
- ✅ 大量数据:考虑生成器表达式(节省内存)
七、新手常见易错点
- 混淆容器符号
-
[]→ 列表{}→ 集合/字典(字典需要key:value)()→ 生成器(不是元组)
- 忘记可迭代对象
错误示例:[i for i]→ 必须写for i in 可迭代对象(比如range(10)、列表等) - 条件位置错误
正确:[i for i in range(10) if i > 5]
错误:[i if i > 5 for i in range(10)](语法错误,条件要放在遍历后面) - 嵌套循环顺序
嵌套推导式中,for的顺序和嵌套循环完全一致:
# 循环写法
for i in A:
for j in B:
...
# 推导式写法
[... for i in A for j in B]
- zip 打包后重复遍历
zip 对象是“一次性的”,转成列表/遍历一次后就空了,比如:
zipped = zip(names_list, ages_list)
print(list(zipped)) # 第一次遍历:能看到内容
print(list(zipped)) # 第二次遍历:空列表 []
八、总结
推导式是 Python 新手进阶的关键语法,核心记住这几个要点:
- 列表推导式:
[表达式 for ... if ...]→ 最常用,生成有序可重复的列表; - 集合推导式:
{表达式 for ... if ...}→ 自动去重,无序; - 字典推导式:
{键:值 for ... if ...}→ 生成键值对,可结合zip()简化两个列表的合并; zip()核心作用是“按位置打包多个列表”,搭配字典推导式更简洁;- 没有元组推导式,
(...)是生成器,转元组需用tuple()。
推导式的核心是“简化循环”,新手先吃透基础循环,再用推导式优化代码,不用急于求成
更多推荐



所有评论(0)