如何在已关闭的 Python 生成器上恢复迭代

PythonPythonBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

Python 生成器是用于高效且内存优化的数据处理的强大工具。在本教程中,我们将深入了解如何在已关闭的 Python 生成器上恢复迭代,探索实际应用场景并为常见挑战提供实用解决方案。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/AdvancedTopicsGroup(["Advanced Topics"]) python/AdvancedTopicsGroup -.-> python/iterators("Iterators") python/AdvancedTopicsGroup -.-> python/generators("Generators") python/AdvancedTopicsGroup -.-> python/context_managers("Context Managers") subgraph Lab Skills python/iterators -.-> lab-398060{{"如何在已关闭的 Python 生成器上恢复迭代"}} python/generators -.-> lab-398060{{"如何在已关闭的 Python 生成器上恢复迭代"}} python/context_managers -.-> lab-398060{{"如何在已关闭的 Python 生成器上恢复迭代"}} end

理解 Python 生成器

Python 生成器是一种特殊类型的函数,它允许你创建迭代器。与普通函数不同,普通函数返回一个值后就会终止,而生成器可以暂停和恢复,从而能够随着时间的推移生成一系列值。

普通函数和生成器函数之间的关键区别在于使用 yield 关键字而不是 return。当调用生成器函数时,它会返回一个生成器对象,可以对其进行迭代以获取它生成的值。

下面是一个生成器函数的简单示例:

def count_up_to(n):
    i = 0
    while i < n:
        yield i
        i += 1

在这个示例中,count_up_to() 函数是一个生成器,它生成从 0 到(但不包括)给定值 n 的一系列数字。当调用该函数时,它会返回一个生成器对象,可以对其进行迭代以获取这些值。

>>> counter = count_up_to(5)
>>> for num in counter:
...     print(num)
0
1
2
3
4

生成器通常用于需要生成大量或可能无限的数值序列,但又不想一次性将整个序列存储在内存中的情况。在处理大型数据集或需要进行某种流式或实时数据处理时,这可能特别有用。

graph TD A[函数调用] --> B[生成器对象] B --> C[生成值] C --> B B --> D[迭代完成]

在上面的图表中,我们可以看到 Python 生成器的生命周期。当调用生成器函数时,它会返回一个生成器对象。然后可以对这个对象进行迭代,每次对生成器对象调用 next() 函数时,生成器函数会恢复执行,直到到达 yield 语句,此时它会生成值并暂停执行。这个过程会一直持续,直到生成器函数完成,此时迭代结束。

关闭并恢复生成器迭代

关闭生成器

可以使用 close() 方法关闭生成器,该方法允许你停止生成器的执行,并防止它再生成任何值。当生成器被关闭后,任何后续尝试对其进行迭代都会引发 GeneratorExit 异常。

下面是一个示例:

def count_up_to(n):
    i = 0
    try:
        while i < n:
            yield i
            i += 1
    finally:
        print("清理资源...")

counter = count_up_to(5)
print(next(counter))  ## 输出:0
counter.close()
print(next(counter))  ## 引发 GeneratorExit 异常

在这个示例中,count_up_to() 生成器函数使用了一个 try-finally 块,以确保在生成器关闭时执行一些清理代码。当在生成器对象上调用 close() 方法时,会引发 GeneratorExit 异常,然后执行 finally 块。

在已关闭的生成器上恢复迭代

一旦生成器被关闭,就不能直接恢复。但是,你可以创建一个新的生成器实例,并从先前生成器停止的地方继续迭代。这可以通过使用 yield from 语法来实现,该语法允许你将迭代委托给另一个生成器。

下面是一个示例:

def count_up_to(n):
    i = 0
    try:
        while i < n:
            yield i
            i += 1
    finally:
        print("清理资源...")

def resume_count(counter, start_from):
    try:
        yield from itertools.islice(counter, start_from, None)
    except GeneratorExit:
        print("生成器已关闭,创建新实例。")
        counter = count_up_to(n)
        yield from itertools.islice(counter, start_from, None)

n = 10
counter = count_up_to(n)
print(list(itertools.islice(counter, 3)))  ## 输出:[0, 1, 2]
counter.close()

new_counter = resume_count(counter, 3)
print(list(new_counter))  ## 输出:[3, 4, 5, 6, 7, 8, 9]

在这个示例中,resume_count() 函数接受一个已关闭的生成器和一个起始索引,然后使用 yield from 语法将迭代委托给原始生成器。如果原始生成器已关闭,该函数会创建一个新的生成器实例,并从指定的起始索引继续迭代。

itertools.islice() 函数用于从指定索引开始选择生成器输出的一个切片。

生成器在实际中的应用场景

Python 中的生成器在实际中有广泛的应用。以下是一些示例:

文件处理

生成器可用于高效处理大型文件,通过分块读取和处理数据,而不是一次性将整个文件加载到内存中。在处理日志文件、CSV 文件或其他大型数据源时,这特别有用。

def read_file_in_chunks(filename, chunk_size=1024):
    with open(filename, 'r') as file:
        while True:
            chunk = file.read(chunk_size)
            if not chunk:
                break
            yield chunk

无限序列

生成器可用于生成无限序列,例如斐波那契数列或质数序列。这在各种应用中都很有用,如模拟、数据分析或算法问题。

def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

协程

生成器可用于实现协程,协程是一种并发形式,允许多个任务并发执行,而无需创建单独线程的开销。协程可用于构建可扩展的网络服务器、异步 I/O 系统和其他并发应用程序。

@LabEx.coroutine
def echo_server(client_conn):
    while True:
        data = yield from client_conn.read()
        if not data:
            break
        yield from client_conn.write(data)

数据管道

生成器可用于创建数据管道,其中数据按一系列步骤进行处理,每个步骤都实现为一个生成器函数。这在各种数据处理任务中都很有用,如 ETL(提取、转换、加载)工作流、数据清理和预处理等。

def extract_data(filename):
    for row in read_csv(filename):
        yield row

def transform_data(data):
    for row in data:
        yield transform_row(row)

def load_data(data):
    for row in data:
        save_to_database(row)

pipeline = load_data(transform_data(extract_data('data.csv')))
for _ in pipeline:
    pass

这些只是 Python 中生成器众多实际应用场景的几个示例。通过利用生成器的强大功能,你可以编写更高效、可扩展和易于维护的代码,以应对各种数据处理和并发挑战。

总结

在本教程结束时,你将对 Python 生成器、如何处理已关闭的生成器以及在需要时恢复迭代的实用技巧有扎实的理解。这些知识将使你能够充分利用生成器函数的全部潜力,编写更高效、更健壮的 Python 代码。