Python 中的列表推导式与生成器表达式

更新于 2026-01-13

Soner Ayberk 2024-10-28

你知道以下语法之间的区别吗?

[x for x in range(5)]
(x for x in range(5))
tuple(range(5))

这正是 Python 与其他语言的区别所在,也是让 Python 成为最佳编程语言的原因之一。列表推导式(List Comprehension)源于函数式编程思想,并在 Python 早期就被引入,如今已成为其标志性特性。

本文是关于 Python 列表推导式和生成器表达式的完整指南,提供了大量实用示例。

你将了解:

  • Python 中列表推导式和生成器表达式的写法。
  • 如何正确创建它们。
  • 它们最适合在哪些场景中使用。
  • 可迭代对象(Iterable)与迭代器(Iterator)的区别。
  • 使用列表推导式和生成器表达式的好处。

关于列表的 4 个事实

首先,我们简要回顾一下列表(在其他语言中通常称为数组)。

  • 列表是一种数据类型,表示元素的集合。一个简单的列表如下所示:[0, 1, 2, 3, 4, 5]
  • 列表可以包含任意类型的数据及其组合
    >>> a = 12
    >>> b = "this is text"
    >>> my_list = [0, b, ['element', 'another element'], (1, 2, 3), a]
    >>> print(my_list)
    [0, 'this is text', ['element', 'another element'], (1, 2, 3), 12]
    
  • 列表支持索引。你可以通过索引访问单个元素或一组元素:
    >>> a = ['red', 'green', 'blue']
    >>> print(a[0])
    red
    
  • 列表在 Python 中是可变的(mutable),这意味着你可以替换、添加或删除其中的元素。

如何在 Python 中创建列表

创建列表有两种常见方式:

>>> my_list = [0, 1, 1, 2, 3]

或者不太常用的方式:

>>> my_list = list()

通常,list(obj) 用于将其他序列转换为列表。例如,将字符串拆分为单个字符:

>>> string = "string"
>>> list(string)
['s', 't', 'r', 'i', 'n', 'g']

什么是列表推导式?

列表推导式常被视为 Python 函数式编程的一部分,它能让你用更少的代码创建列表。相比传统的 for 循环,列表推导式不仅执行更快、效率更高,还能提升代码的可读性和简洁性。简而言之,这是一种非常“Pythonic”的编码方式:代码越少,效果越好

来看下面这个例子:

使用 for 循环和 range() 函数创建列表:

>>> my_list = []
>>> for x in range(10):
...     my_list.append(x * 2)
...
>>> print(my_list)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

而使用列表推导式实现相同功能:

>>> comp_list = [x * 2 for x in range(10)]
>>> print(comp_list)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

上面的例子是为了说明语法,其实也可以直接用 list(range(0, 19, 2)) 实现。但列表推导式的真正优势在于:你可以在第一部分使用更复杂的表达式,或添加条件来过滤元素。例如:

>>> comp_list = [x ** 2 for x in range(7) if x % 2 == 0]
>>> print(comp_list)
[0, 4, 16, 36]

你还可以用列表推导式组合多个列表,创建嵌套列表。虽然语法初看有些复杂,但可以将其理解为“外层”和“内层”序列的组合。

例如,将两个简单列表组合成一个列表的列表:

>>> nums = [1, 2, 3, 4, 5]
>>> letters = ['A', 'B', 'C', 'D', 'E']
>>> nums_letters = [[n, l] for n in nums for l in letters]
>>> print(nums_letters)
[[1, 'A'], [1, 'B'], [1, 'C'], [1, 'D'], [1, 'E'],
 [2, 'A'], [2, 'B'], [2, 'C'], [2, 'D'], [2, 'E'],
 [3, 'A'], [3, 'B'], [3, 'C'], [3, 'D'], [3, 'E'],
 [4, 'A'], [4, 'B'], [4, 'C'], [4, 'D'], [4, 'E'],
 [5, 'A'], [5, 'B'], [5, 'C'], [5, 'D'], [5, 'E']]

再来看一个处理字符串的例子:

>>> iter_string = "some text"
>>> comp_list = [x for x in iter_string if x != " "]
>>> print(comp_list)
['s', 'o', 'm', 'e', 't', 'e', 'x', 't']

推导式不仅限于列表,你还可以创建字典推导式和集合推导式:

>>> dict_comp = {x: chr(65+x) for x in range(1, 11)}
>>> type(dict_comp)
<class 'dict'>
>>> print(dict_comp)
{1: 'B', 2: 'C', 3: 'D', 4: 'E', 5: 'F', 6: 'G', 7: 'H', 8: 'I', 9: 'J', 10: 'K'}

>>> set_comp = {x ** 3 for x in range(10) if x % 2 == 0}
>>> type(set_comp)
<class 'set'>
>>> print(set_comp)
{0, 8, 64, 512, 216}

何时使用列表推导式?

当你需要对大量数据应用相同的逻辑或函数时,列表推导式是提升代码可读性的最佳选择。例如,在 KYC(客户身份验证)场景中:

传统方式:

>>> customers = [{"is_kyc_passed": False}, {"is_kyc_passed": True}]
>>> kyc_results = []
>>> for customer in customers:
...     kyc_results.append(customer["is_kyc_passed"])
...
>>> all(kyc_results)
False

使用列表推导式(甚至更进一步,使用生成器表达式):

>>> customers = [{"is_kyc_passed": False}, {"is_kyc_passed": True}]
>>> all(customer["is_kyc_passed"] for customer in customers)
False

使用列表推导式的好处

  • 避免副作用:不会产生冗余的临时变量。
  • 代码更简洁易读:用一行代替多行 for 循环。
  • 执行速度更快:列表推导式在 C 层面优化,比普通循环快。
  • 符合函数式编程范式:不修改原列表,而是创建新列表。

可迭代对象(Iterable)与迭代器(Iterator)的区别

理解生成器前,需先掌握这两个概念。

  • 可迭代对象(Iterable):是可以被遍历的对象,如列表、字符串、字典、元组、集合等。只要对象实现了 __iter__() 方法,就是可迭代的。

    >>> hasattr(str, '__iter__')
    True
    >>> hasattr(bool, '__iter__')
    False
    
  • 迭代器(Iterator):是实现了迭代器协议的对象。当你使用 for 循环时,背后发生的是:

    1. 调用 iter() 将可迭代对象转为迭代器。
    2. 反复调用 next() 获取下一个元素。
    3. 当没有元素时,抛出 StopIteration 异常。
    >>> simple_list = [1, 2, 3]
    >>> my_iterator = iter(simple_list)
    >>> next(my_iterator)
    1
    >>> next(my_iterator)
    2
    >>> next(my_iterator)
    3
    >>> next(my_iterator)
    Traceback (most recent call last):
    StopIteration
    

生成器表达式(Generator Expressions)

在 Python 中,生成器是一种便捷实现迭代器协议的方式。生成器是通过包含 yield 语句的函数创建的。

生成器表达式的核心特点是按需计算元素(惰性求值)。普通函数遇到 return 就终止,而带 yield 的函数会保存状态,下次调用时从中断处继续。

你可以这样定义生成器函数:

>>> def my_gen():
...     for x in range(5):
...         yield x

生成器表达式则允许你在不使用 yield 的情况下快速创建生成器,语法与列表推导式几乎相同,只是用圆括号代替方括号:

>>> gen_exp = (x ** 2 for x in range(10) if x % 2 == 0)
>>> for x in gen_exp:
...     print(x)
0
4
16
36
64

注意:两者返回的类型不同:

>>> list_comp = [x ** 2 for x in range(10) if x % 2 == 0]
>>> gen_exp = (x ** 2 for x in range(10) if x % 2 == 0)
>>> print(list_comp)
[0, 4, 16, 36, 64]
>>> print(gen_exp)
<generator object <genexpr> at 0x7f600131c410>

内存效率对比

生成器的最大优势是内存占用极小

>>> from sys import getsizeof
>>> my_comp = [x * 5 for x in range(1000)]   # 列表推导式
>>> my_gen = (x * 5 for x in range(1000))    # 生成器表达式
>>> getsizeof(my_comp)
9024
>>> getsizeof(my_gen)
88

这是因为列表在创建时就分配了全部内存并计算所有值,而生成器只保存“如何生成下一个值”的指令,按需计算。

⚠️ 注意:生成器虽然省内存,但每次生成元素时都需要保存和恢复函数状态,因此速度略慢于列表

如果需要将生成器转为列表,可以使用 list() 或解包操作符 *

>>> list(gen_exp)
# 或
>>> [*gen_exp]

总结

列表推导式是基于现有列表创建新列表的便捷方式,比传统方法更快、更紧凑。即使是 Python 初学者也应掌握其正确用法。

但要注意:过长的列表推导式会降低可读性,反而不利于维护。因此,应在简洁与清晰之间取得平衡。

合理使用列表推导式和生成器表达式,不仅能写出更优雅的代码,还能显著提升程序性能和资源利用率。