Python中的生成器表达式与生成器

Python
Author

Tom

Published

September 11, 2024

生成器表达式(generator expression)是 Python 中的一种简洁方式,用来在迭代时生成数据,而不是一次性将所有结果存储在内存中。它与列表推导式(list comprehension)类似,但它不返回整个列表,而是返回一个生成器对象,通过惰性求值节省内存。

生成器表达式与列表推导式

列表推导式可以快速从一个可迭代对象生成一个新的列表。比如我们从 L 生成一个新的列表 x:

# list expression
L = [1, 2, 3]
x = [i**2 for i in L]
print(x)
[1, 4, 9]

生成器表达式则返回一个生成器对象,可以通过 next() 函数计算求值:

# generator expression
L = [1, 2, 3]
x = (i**2 for i in L)
print(x)
print(next(x))
<generator object <genexpr> at 0x7f3276ef7440>
1

next() 函数会记录上一次打印的值从上一次中断处继续执行,直到循环结束抛出异常:

print(next(x))
print(next(x))
print(next(x))
4
9
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
Cell In[4], line 3
      1 print(next(x))
      2 print(next(x))
----> 3 print(next(x))

StopIteration: 

以上当 3 次求值再进行求值便抛出异常。同理也可以使用 for 循环对生成器表达式进行迭代:

L = [1, 2, 3]
x = (i**2 for i in L)
for i in x:
    print(i)
1
4
9

生成器与迭代器

在 Python 中,迭代器(Iterator)是一种用于遍历元素的对象,允许你逐个访问容器(如列表、元组、字典、集合等)中的元素,而无需关心容器的底层实现。Python 中迭代器必须实现两个方法:__iter__()__next__()

  • __iter__():返回迭代器对象本身。这个方法使对象可以被 iter() 函数调用。
  • __next__():返回容器的下一个元素。当没有元素时,会引发 StopIteration 异常来结束迭代。

迭代器不会在内存中一次性生成所有元素,而是在每次调用 next() 方法是生成下一个值。且迭代器只能遍历一次,一旦迭代完毕就会耗尽,不能再次使用。

生成器是一种特殊的迭代器。生成器通过使用 yield 关键字定义,与普通函数不同的是,生成器函数在执行时会保存它的状态(局部变量、位置等),每次调用时可以从上次停止的地方继续执行。这种特性使得生成器非常适合处理大量数据或无限序列,因为它可以按需产生值,而不需要一次性将所有数据加载到内存中。

# 定义生成器
def generator_test():
    for i in [1, 2, 3]:
        yield i
        
# 使用生成器
gen = generator_test()
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
1
2
3
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
Cell In[6], line 11
      9 print(next(gen))
     10 print(next(gen))
---> 11 print(next(gen))

StopIteration: 

在上面的代码中,我们定义了一个生成器函数,它在每次执行到 yield 时返回一个值并暂停执行,直到被再次调用。通过 next() 逐步求值。迭代 3 次完毕后再次求值抛出异常。

Python 也提供了一些内建的生成器函数,可以简化某些常见的迭代任务,并节约内存空间,比如 range()enumerate()zip() 等。

迭代器与可迭代对象

在 Python 中,可迭代对象(Iterable)是指可以逐个返回其元素的对象。可迭代对象实现了 __iter__() 方法,允许你使用 for 循环或其他迭代方式遍历其元素。常见的可迭代对象包括列表、元组、字符串、字典、集合等。

我们可以使用 Python 标准库中的 collections.abc.Iterable 来检查对象是否是可迭代的:

from collections.abc import Iterable

print(isinstance([1, 2, 3, 4], Iterable)) 
print(isinstance(1234, Iterable))       
True
False

可迭代对象可以被 iter() 函数转化为迭代器:

x = [1, 2, 3, 4]
print(iter(x))
<list_iterator object at 0x7f3276f269b0>

事实上,当我们使用 for 循环或其他迭代操作时,Python 会在后台调用 iter() 函数将可迭代对象转化为迭代器,然后使用 next() 函数逐个获取元素,直到捕获 StopIteration 异常,但不会抛出该异常。