哈喽~今天我们来啃一啃 Python 里超实用的推导式!它是 Python 独有的简洁语法,核心就是用一行代码替代多行循环+赋值操作,让代码更清爽、执行效率也更高,不管是生成列表、集合还是字典,都能轻松搞定

一、推导式的通用格式

先搞懂通用结构,后面所有推导式都能举一反三

# 通用骨架(中括号/大括号决定生成的容器类型)
新容器 = [表达式 for 变量 in 可迭代对象 if 条件]

逐部分拆解(新手必看)

组成部分

通俗解释

示例

新容器

最终生成的结果容器(列表/集合/字典)

my_list / my_set / my_dict

[] / {} / {:}

容器标志:
[] → 列表
{} → 集合/字典(字典需加key:value

列表用[],集合/字典用{}

表达式

最终要存入容器的内容(可以是变量、运算、函数调用)

i / i**2 / names_list[i]

for 变量 in 可迭代对象

遍历的核心逻辑:
依次把可迭代对象(range、列表、元组等)的元素赋值给变量

for i in range(10)(遍历0-9)

if 条件(可选)

过滤规则:只有满足条件的元素才会被存入容器

if i % 2 == 0(只留偶数)

小提醒:推导式本质是语法糖,底层还是循环,只是写法更简洁,新手先理解循环,再看推导式会轻松很多~

二、列表推导式(最常用!先从它入手)

列表是 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=05%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(索引)

依赖range(len(列表))

新手易理解,和基础循环逻辑一致

代码稍长,需手动处理索引

用 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 循环

多(多行)

高(步骤清晰)

稍慢

复杂逻辑、多步操作、异常处理

选择建议

  • ✅ 简单转换/过滤:用推导式(代码更优雅)
  • ✅ 复杂业务逻辑:用循环(便于调试和扩展)
  • ✅ 大量数据:考虑生成器表达式(节省内存)

七、新手常见易错点

  1. 混淆容器符号
    • [] → 列表
    • {} → 集合/字典(字典需要 key:value
    • () → 生成器(不是元组)
  1. 忘记可迭代对象
    错误示例:[i for i] → 必须写 for i in 可迭代对象(比如range(10)、列表等)
  2. 条件位置错误
    正确:[i for i in range(10) if i > 5]
    错误:[i if i > 5 for i in range(10)](语法错误,条件要放在遍历后面)
  3. 嵌套循环顺序
    嵌套推导式中,for 的顺序和嵌套循环完全一致:
# 循环写法
for i in A:
    for j in B:
        ...
# 推导式写法
[... for i in A for j in B]
  1. zip 打包后重复遍历
    zip 对象是“一次性的”,转成列表/遍历一次后就空了,比如:
zipped = zip(names_list, ages_list)
print(list(zipped))  # 第一次遍历:能看到内容
print(list(zipped))  # 第二次遍历:空列表 []

八、总结

推导式是 Python 新手进阶的关键语法,核心记住这几个要点:

  1. 列表推导式:[表达式 for ... if ...] → 最常用,生成有序可重复的列表;
  2. 集合推导式:{表达式 for ... if ...} → 自动去重,无序;
  3. 字典推导式:{键:值 for ... if ...} → 生成键值对,可结合zip()简化两个列表的合并;
  4. zip() 核心作用是“按位置打包多个列表”,搭配字典推导式更简洁;
  5. 没有元组推导式,(...) 是生成器,转元组需用tuple()

推导式的核心是“简化循环”,新手先吃透基础循环,再用推导式优化代码,不用急于求成

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐