python yield关键字

9 阅读4分钟

在 Python 中,yield 是一个非常强大的关键字,它用于定义生成器(generator)。生成器是一种特殊的迭代器,允许在函数中保存状态并在多次调用之间恢复状态。以下是对 yield 的一些demo,包括它的语法、用途、工作原理和一些实际应用场景。

1. yield 的基本语法

yield 关键字通常用在函数中,当函数中包含 yield 时,这个函数就变成了一个生成器函数。生成器函数的调用方式与普通函数类似,但它返回的是一个生成器对象,而不是一个具体的值。

def my_generator():
    yield 1
    yield 2
    yield 3

2. 生成器的工作原理

生成器函数在执行过程中,每次遇到 yield 时,会暂停执行,并将 yield 后面的值返回给调用者。当再次调用生成器的 next() 方法时,函数会从上次暂停的地方继续执行,直到遇到下一个 yield 或函数结束。

示例代码

def my_generator():
    yield 1
    yield 2
    yield 3

gen = my_generator()
print(next(gen))  # 输出 1
print(next(gen))  # 输出 2
print(next(gen))  # 输出 3
# 再次调用 next(gen) 会抛出 StopIteration 异常,因为生成器已经没有更多的值可以返回

3. 生成器的优势

  • 内存效率:生成器按需生成值,不会一次性将所有值存储在内存中,这在处理大量数据时非常有用,可以节省内存。
  • 惰性求值:生成器是惰性求值的,只有在需要时才会计算下一个值,这可以提高程序的性能。
  • 状态保存:生成器函数在每次暂停时会保存其状态,包括局部变量、指令指针等,这使得生成器可以方便地恢复执行。

4. yield 的高级用法

4.1 生成器表达式

生成器表达式类似于列表推导式,但返回的是一个生成器对象,而不是一个列表。

gen = (x * x for x in range(5))
print(next(gen))  # 输出 0
print(next(gen))  # 输出 1
print(next(gen))  # 输出 4
print(next(gen))  # 输出 9
print(next(gen))  # 输出 16
# 再次调用 next(gen) 会抛出 StopIteration 异常

4.2 yield from

yield from 用于在生成器中嵌套另一个生成器,它可以简化嵌套生成器的代码。

def my_generator():
    yield from range(3)
    yield from range(3, 6)

gen = my_generator()
for value in gen:
    print(value)
# 输出 0, 1, 2, 3, 4, 5

4.3 生成器的 .send() 方法

生成器不仅可以通过 yield 返回值,还可以通过 .send() 方法向生成器发送值。这使得生成器可以实现双向通信。

def my_generator():
    while True:
        received = yield
        print(f"收到的值是: {received}")

gen = my_generator()
next(gen)  # 启动生成器
gen.send(10)  # 输出 "收到的值是: 10"
gen.send(20)  # 输出 "收到的值是: 20"

5. 实际应用场景

5.1 文件读取

生成器可以用于逐行读取大文件,避免一次性将整个文件加载到内存中。

def read_file(filename):
    with open(filename, 'r') as file:
        for line in file:
            yield line.strip()

for line in read_file('large_file.txt'):
    print(line)

5.2 数据流处理

生成器可以用于处理数据流,例如在处理网络请求时逐个处理响应数据。

def process_data(data_stream):
    for data in data_stream:
        yield process(data)

def data_stream():
    # 模拟数据流
    for i in range(10):
        yield f"data {i}"

for processed_data in process_data(data_stream()):
    print(processed_data)

5.3 管道和过滤器

生成器可以用于实现管道和过滤器模式,将多个生成器组合起来处理数据。

def filter_even(numbers):
    for number in numbers:
        if number % 2 == 0:
            yield number

def square(numbers):
    for number in numbers:
        yield number * number

numbers = range(10)
even_numbers = filter_even(numbers)
squared_numbers = square(even_numbers)

for number in squared_numbers:
    print(number)
# 输出 0, 4, 16, 36, 64

6. 总结

yield 是一个非常强大的关键字,它使得 Python 中的生成器功能非常强大。生成器不仅可以按需生成值,节省内存,还可以通过 .send() 方法实现双向通信。在实际开发中,生成器可以用于处理大文件、数据流以及实现管道和过滤器模式等多种场景,极大地提高了代码的可读性和性能。