Python列表推导式:一行代码搞定复杂循环

一、导语

刚学Python时,你是不是也写过这样的代码——先创建一个空列表,然后写一个for循环,再一行一行往里面append数据?这段代码通常要占四五行,每次写都觉得很啰嗦。其实Python早就为我们准备了一个优雅的替代方案:列表推导式(List Comprehension)

列表推导式是Python最受欢迎的特性之一。它可以把多行的循环+条件逻辑压缩成一行,代码不仅更短,读起来也更接近"人类语言"。本文带你从入门到进阶,彻底掌握这个利器。

二、基础语法:从循环到推导式

先看一个最简单的场景:生成1到10的平方数列表。

传统写法(3行):

squares = []
for i in range(1, 11):
    squares.append(i ** 2)
print(squares)  # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

列表推导式(1行):

squares = [i ** 2 for i in range(1, 11)]
print(squares)  # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

语法拆解一下:[表达式 for 变量 in 可迭代对象]

  • for i in range(1, 11) — 和普通循环的for一样,依次取出每个元素
  • i ** 2 — 对每个元素执行的表达式,结果直接放入新列表
  • 外面用[]包起来,表示我们要的是一个列表

把这个结构记牢,后面所有变体都是在这个基础上的扩展。

三、加上条件过滤:if的妙用

现实场景中,我们经常需要筛选数据。比如从一堆数字里挑出偶数再求平方。

传统写法:

even_squares = []
for i in range(1, 21):
    if i % 2 == 0:
        even_squares.append(i ** 2)

列表推导式:

even_squares = [i ** 2 for i in range(1, 21) if i % 2 == 0]
print(even_squares)  # [4, 16, 36, 64, 100, 144, 196, 256, 324, 400]

语法变为:[表达式 for 变量 in 可迭代对象 if 条件]

注意if写在for后面,而不是表达式前面。这恰好符合我们阅读的顺序——“从1到20里取出i,如果i是偶数,就计算i的平方”。

四、if-else:条件表达式放前面

如果筛选还不够,需要对不同情况做不同处理,就用if-else。但要记住一个重要区别:if-else写在表达式位置,if(无else)写在循环后面

# 偶数求平方,奇数保持不变
result = [i ** 2 if i % 2 == 0 else i for i in range(1, 11)]
print(result)  # [1, 4, 3, 16, 5, 36, 7, 64, 9, 100]

结构是:[结果A if 条件 else 结果B for i in ...]。这是Python的三元表达式嵌入到了推导式中。

对比记忆法:

场景 写法 位置
只要符合条件的 if 条件 for 后面
符合条件的做A,否则做B A if 条件 else B for 前面(表达式位置)

五、嵌套循环:多维数据的处理利器

遇到二维列表(矩阵)时,列表推导式同样能大显身手。

展平二维列表:

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# 传统写法
flat = []
for row in matrix:
    for num in row:
        flat.append(num)

# 推导式:for的顺序和传统嵌套一致
flat = [num for row in matrix for num in row]
print(flat)  # [1, 2, 3, 4, 5, 6, 7, 8, 9]

注意for的书写顺序:外层循环在前,内层循环在后,这和普通嵌套循环的书写顺序完全一致,非常好记。

再加条件过滤:

# 只提取大于5的元素
flat_big = [num for row in matrix for num in row if num > 5]
print(flat_big)  # [6, 7, 8, 9]

六、实战场景:五个工作中常用的例子

6.1 清洗字符串列表

names = ["  Alice ", "BOB", "  Charlie  ", "david"]
clean = [name.strip().title() for name in names]
print(clean)  # ['Alice', 'Bob', 'Charlie', 'David']

6.2 筛选有效邮箱

emails = ["alice@qq.com", "invalid", "bob@163.com", "not-an-email", "charlie@gmail.com"]
valid = [e for e in emails if "@" in e and "." in e.split("@")[-1]]
print(valid)  # ['alice@qq.com', 'bob@163.com', 'charlie@gmail.com']

6.3 字典列表提取字段

users = [
    {"name": "张三", "age": 28, "city": "北京"},
    {"name": "李四", "age": 35, "city": "上海"},
    {"name": "王五", "age": 22, "city": "深圳"},
]
names = [u["name"] for u in users]
print(names)  # ['张三', '李四', '王五']

# 30岁以上用户的城市
cities = [u["city"] for u in users if u["age"] > 30]
print(cities)  # ['上海']

6.4 文件路径批量处理

import os

# 获取当前目录下所有.py文件的绝对路径
py_files = [
    os.path.abspath(f)
    for f in os.listdir(".")
    if f.endswith(".py")
]

6.5 生成笛卡尔积

colors = ["红", "蓝", "绿"]
sizes = ["S", "M", "L"]
combinations = [f"{c}-{s}" for c in colors for s in sizes]
print(combinations)
# ['红-S', '红-M', '红-L', '蓝-S', '蓝-M', '蓝-L', '绿-S', '绿-M', '绿-L']

七、不止列表:字典推导式与集合推导式

学会了列表推导式,另外两种推导式就是换一对括号的事。

字典推导式:

# 把列表转成字典:名字→长度
words = ["python", "java", "golang"]
word_len = {w: len(w) for w in words}
print(word_len)  # {'python': 6, 'java': 4, 'golang': 6}

# 键值互换
d = {"a": 1, "b": 2, "c": 3}
reversed_d = {v: k for k, v in d.items()}
print(reversed_d)  # {1: 'a', 2: 'b', 3: 'c'}

集合推导式:

# 获取字符串中所有不重复的字母
text = "hello world"
unique_chars = {c for c in text if c != " "}
print(unique_chars)  # {'d', 'e', 'h', 'l', 'o', 'r', 'w'}

八、注意事项:推导式不是万能药

列表推导式虽好,但过度使用会让代码变得难以阅读:

# 反面教材:嵌套三层还带条件,读起来很痛苦
result = [x * y for x in range(10) if x % 2 == 0 for y in range(10) if y % 3 == 0]

使用原则:

  • 两层嵌套是上限:超过两层,老老实实用传统for循环
  • 单个推导式不超过一行:如果一行写不完(超过约80字符),拆成多行或用普通循环
  • 复杂逻辑拆出来:如果表达式本身就很复杂,先定义成函数,再在推导式中调用
# 拆成函数,推导式就清晰了
def normalize(s):
    return s.strip().lower().replace("-", "_")

fields = ["  User-Name ", "Email-Address  ", "  Phone-Number"]
result = [normalize(f) for f in fields]
print(result)  # ['user_name', 'email_address', 'phone_number']

九、总结

列表推导式是Python中"少即是多"哲学的典型体现。核心要点回顾:

要素 说明
基本语法 [表达式 for 变量 in 可迭代对象]
条件筛选 [表达式 for 变量 in 可迭代对象 if 条件]
条件分支 [A if 条件 else B for 变量 in 可迭代对象]
嵌套循环 [表达式 for 外层 in 容器1 for 内层 in 容器2]
字典推导 {键: 值 for ... in ...}
集合推导 {元素 for ... in ...}

掌握了列表推导式,你会发现很多原本需要五六行的循环逻辑,一行就能优雅地解决。代码更简洁,也更Pythonic。下次写循环前,不妨先想想:能不能用推导式搞定?


更多推荐