python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python yield函数

Python中yield函数的用法详解

作者:大神神奇奇奇

这篇文章详细介绍了Python中的yield关键字及其用法,yield关键字用于生成器函数中,使得函数可以像迭代器一样工作,但不会一次性将所有结果加载到内存中,文中将用法介绍的非常详细,需要的朋友可以参考下

python官方api地址

1. yield介绍

在Python中,yield关键字主要用于生成器函数(generator functions)中,其目的是使函数能够像迭代器一样工作,即可以被遍历,但不会一次性将所有结果都加载到内存中。

2.yield基本用法

def simple_generator():
    yield 1
    yield 2
    yield 3

gen = simple_generator()
print(next(gen))  # 输出: 1
print(next(gen))  # 输出: 2
print(next(gen))  # 输出: 3
def simple_generator():
    yield 1
    yield 2
    yield 3

for value in simple_generator():
    print(value)
gen_expr = (x * x for x in range(5))
for value in gen_expr:
    print(value)
def simple_generator():
    yield 1
    yield 2
    yield 3
gen = simple_generator()
print(list(gen))  # 输出: [1, 2, 3]

3.yield高级用法

3.1 yield send() 方法

生成器不仅可以通过 yield 返回值,还可以通过 send() 方法接收外部输入。

send() 方法允许向生成器发送一个值,这个值将成为上一个 yield 表达式的值。

这种方式可以实现生成器与外部之间的双向通信。

def coroutine():
    while True:
        received = yield
        print(f"接收到的数据: {received}")

co = coroutine()
next(co)  # 预激生成器
co.send(10)  # 输出: 接收到的数据: 10
co.send(20)  # 输出: 接收到的数据: 20

在这个例子中,coroutine 生成器函数在每次迭代时接收外部数据并打印它。
next(co) 用于预激生成器,使其准备好接收数据。

3.2 yield from方法

从Python 3.3开始,引入了 yield from 语法,它允许一个生成器委托另一个生成器来生成值。

这种委托机制可以简化嵌套生成器的使用,提高代码的可读性和效率。

def base_code_pool():
    for i in range(3):
        yield f'BASE-{i + 1}'

def outsource_pool():
    for i in range(30):
        yield f'OUTS-{i + 1}'

def team_member_code():
    yield from base_code_pool()
    print('内部资源编号用完,开始使用外包')
    yield from outsource_pool()

team_member = team_member_code()
for i in range(5):
    print(next(team_member))

运行结果:

BASE-1
BASE-2
BASE-3
内部资源编号用完,开始使用外包
OUTS-1
OUTS-2

在这个例子中,team_member_code 生成器函数委托 base_code_pool 和 outsource_pool 生成器来生成值。
当 base_code_pool 的值用完后,生成器会继续从 outsource_pool 生成值。

3.3 yield 和yield from叠加

yield 和 yield from 是 Python 中用于创建生成器的两种方式,它们允许函数在迭代过程中逐步返回值,而不是一次性返回所有结果。
这种特性使得生成器非常适合处理大型数据集或无穷序列等场景,因为它们不会一次性将所有数据加载到内存中,从而节省了内存资源。

关于是否可以“叠加”yield 和 yield from 的结果,实际上我们讨论的是如何组合多个生成器或者将一个生成器的结果传递给另一个生成器。

def generator_a():
    for i in range(3):
        yield i

def generator_b():
    for i in range(3, 6):
        yield i

def combined_generators():
    # 使用 yield 直接产出每个生成器中的元素
    for value in generator_a():
        yield value
    for value in generator_b():
        yield value
    
    # 或者使用 yield from 委托给子生成器
    yield from generator_a()
    yield from generator_b()

在这个例子中,combined_generators 函数通过 yield 和 yield from 将两个生成器的结果进行了“叠加”。

这里,“叠加”的含义是指顺序地连接了两个生成器产生的输出流,而不是数学意义上的加法操作。

可以将 yield 和 yield from 结合使用来实现生成器之间的组合,甚至可以在同一个函数内部同时使用这两种语句。

这样做不仅可以简化代码结构,还能更灵活地控制生成器的行为。

让我们深入探讨一下如何有效地结合使用 yield 和 yield from 来“叠加”多个生成器的结果。

def sum_of_generators(gen1, gen2):
    return sum(gen1) + sum(gen2)

total = sum_of_generators(generator_a(), generator_b())
print(total)  # 输出: 15 (0+1+2+3+4+5)
# 输出: 0 1 2 3 4 5
for item in combined_generators():
    print(item)

在这里,sum_of_generators 函数接收两个生成器作为参数,并分别对它们求和后再相加。
请注意,这种方法会立即消耗掉这两个生成器的所有元素并计算出总和,这可能不是最有效的做法,特别是对于非常大的数据集。

def generator_a():
    for i in range(3):
        yield i

def generator_b():
    for i in range(3, 6):
        yield i

def combined_generators():
    # 直接使用 yield 产出额外的值
    yield "Start"
    
    # 使用 yield from 委托给子生成器 a
    yield from generator_a()
    
    # 再次直接使用 yield 产出中间的值
    yield "Middle"
    
    # 使用 yield from 委托给子生成器 b
    yield from generator_b()
    
    # 最后直接使用 yield 产出结束的值
    yield "End"

# 输出: Start 0 1 2 Middle 3 4 5 End
for item in combined_generators():
    print(item)

在这个例子中,combined_generators 函数展示了如何在同一个生成器内混合使用 yield 和 yield from

首先,它通过 yield 发出了字符串 "Start"

然后,利用 yield from 将控制权交给 generator_a(),后者负责产出 0, 1, 和 2

接着,再次使用 yield 发出 "Middle";之后,又通过 yield from 让 generator_b() 产出 3, 4, 和 5

最后,以同样的方式发出字符串 "End"

这种方式允许你在不破坏原有逻辑的基础上轻松添加新的生成逻辑,同时也保持了代码的清晰度和可读性。

处理复杂情况下的叠加

如果你面对的是更加复杂的场景,比如你需要对来自不同生成器的数据进行某种形式的处理后再输出,或者你需要根据某些条件决定是否调用某个子生成器,那么你可以继续扩展这种模式。

例如,假设你想把两个生成器的结果相加后作为最终输出的一部分:

def add_generators(gen1, gen2):
    iterator1 = iter(gen1)
    iterator2 = iter(gen2)
    try:
        while True:
            value1 = next(iterator1)
            value2 = next(iterator2)
            yield value1 + value2
    except StopIteration:
        pass

def enhanced_combined_generators():
    yield "Start"
    yield from add_generators(generator_a(), generator_b())
    yield "End"

# 输出: Start 3 5 7 End
for item in enhanced_combined_generators():
    print(item)

综上所述,yield 和 yield from 的结果可以通过编程逻辑被“叠加”,但这取决于你想要实现的具体行为。

如果是简单的迭代输出,则可以直接使用 yield from 来简化代码;

而如果是数值或其他类型的累加,则需要额外的逻辑来完成这个过程。

4.yield主要应用场景

def read_large_file(file_path):
    with open(file_path, 'r') as file:
        for line in file:
            yield line

for line in read_large_file('large_file.txt'):
    print(line, end='')

在这个例子中,生成器函数 read_large_file 逐行读取大文件,并使用 for 循环打印每行内容。
这种方法避免了将整个文件加载到内存中,从而节省了内存。

def coroutine_example():
    while True:
        received = yield
        print(f"处理数据: {received}")

co = coroutine_example()
next(co)  # 预激生成器
co.send('data1')  # 输出: 处理数据: data1
co.send('data2')  # 输出: 处理数据: data2

在这个例子中, coroutine_example 生成器函数可以接收外部数据并处理它,实现了简单的协程功能。

def pipeline_stage1(data):
    for item in data:
        yield item * 2

def pipeline_stage2(data):
    for item in data:
        yield item + 1

data = range(5)
stage1 = pipeline_stage1(data)
stage2 = pipeline_stage2(stage1)

for value in stage2:
    print(value)

在这个例子中,数据经过两个生成器函数处理。

首先在 pipeline_stage1 中乘以2,然后在 pipeline_stage2 中加1。

def add_generators(gen1, gen2):
    iterator1 = iter(gen1)
    iterator2 = iter(gen2)
    try:
        while True:
            value1 = next(iterator1)
            value2 = next(iterator2)
            yield value1 + value2
    except StopIteration:
        pass

def enhanced_combined_generators():
    yield "Start"
    yield from add_generators(generator_a(), generator_b())
    yield "End"
# 输出: Start 3 5 7 End
for item in enhanced_combined_generators():
    print(item)
    这里定义了一个辅助函数 add_generators,它接受两个生成器并逐个取出它们的元素相加以生成新的值。enhanced_combined_generators 则是在之前的基础上加入了这个新功能。通过这种方式,你可以非常灵活地构建复杂的生成器逻辑,而不需要为每一个可能的变化都编写全新的生成器10。

综上所述,yield 和 yield from 可以很好地协同工作,帮助开发者构建高效、易于维护的生成器代码。无论是简单的串联还是更复杂的操作,如数据变换或条件分支,都可以通过合理的设计达到预期的效果。重要的是要理解每种语句的作用以及它们如何相互作用,这样才能写出既强大又优雅的 Python 代码2。此外,yield from 的引入不仅简化了代码,还增强了生成器之间通信的能力,特别是在涉及到异常传递时显得尤为重要14。因此,在实际编程实践中,充分利用这两种工具能够极大地提升代码的质量和性能。

5.总结

到此这篇关于Python中yield函数用法详解的文章就介绍到这了,更多相关Python yield函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文