1. 什么是 yield?
在 Python 里使用 yield 关键字可以将一个函数变成生成器,可以用来迭代遍历数据,避免一次性加载所有的数据到内存,可以帮助我们节省大量的内存,从而提高效率。
1.1 yield 示例
看下面的示例,通过 yield 可以迭代打印一个列表中的每一个元素。 示例代码如下:
def my_generator():
my_list = [1, 2, 3, 4, 5]
for i in my_list:
yield i
generator = my_generator()
for item in generator:
print(item)
上述代码将会依次输出 1, 2, 3, 4, 5 这5个数字。
2. yield from 是什么?
yield from 可以将一个生成器作为子生成器,将生成器的数据以及状态直接传递给主生成器,从而可以更简单的实现复杂的协程操作。 yield from 会在最内层的子生成器和最外层的主体程序之间建立一个双向通道,使得子生成器直接获得调用者(主生成器)的上下文并返回结果。
2.1 yield from 示例
下面的示例展示了如何使用 yield from 来简单的重构之前的生成器。
def my_sub_generator():
my_list = [1, 2, 3, 4, 5]
yield from my_list
def my_generator():
yield from my_sub_generator()
generator = my_generator()
for item in generator:
print(item)
运行上述代码会输出与之前同样的结果:1, 2, 3, 4, 5 。
3. yield 和 yield from 的区别
yield from 是在 Python 3.3 版本后才支持的操作,它主要用在协程中,可以方便地实现迭代器和生成器。而 yield 是 Python 中常见的操作,用于将函数变成生成器。 yield from 是 yield 的进一步封装,尤其在协程中的使用更方便。
3.1 两个语句的使用方式不同
yield 和 yield from 的使用方式不同。在使用 yield 时,需要使用 next() 方法获得下一个生成器(iterable)中的值;而使用 yield from 则需要直接迭代生成器,yield from 从这个生成器中获取值,直到该生成器终止。
3.2 yield 和 yield from 的运行效率不同
当使用 yield 语句时,需要创建一个生成器对象,将其保存在内存中,并为它分配一个堆栈。 yield 语句需要单独完成运行,然后在下一次调用时从上一次离开的位置继续运行,这样可能会导致一些效率问题。而 yield from 则直接将序列中的值传递到连续的函数调用中,没有阻塞堆栈的执行。因此,yield from 在效率上比 yield 更优秀。
3.3 yield 和 yield from 使用场景不同
yield 通常用于生成器的生成,在使用这个语句时,需要根据外部要求生成一个值,更换状态,从而迭代计算得出总的结果。而 yield from 则主要用在协程操作中,可以大大减少编写协程微线程时的代码量,并且使用起来更方便。
4. yield 和 yield from 的应用场景
yield 和 yield from 非常适合于处理大数据量的业务场景,可以避免一次性加载所有的数据到内存,而是通过生成器逐个迭代的处理数据。除此之外,它还经常用于网络编程中,处理异步操作、协程等。
4.1 yield 和 yield from 在协程中的应用
协程是一种微线程,可以在单线程空间中实现多个任务之间的交替运行。使用 yield from 可以让协程像普通的函数一样实现返回,使得代码更加简洁易懂。下面是一个使用协程的示例代码:
import asyncio
async def foo():
print("start")
await asyncio.sleep(1)
print("end")
async def bar():
await foo()
await foo()
loop = asyncio.get_event_loop()
loop.run_until_complete(bar())
loop.close()
运行上述代码,将会输出如下结果:
start
end
start
end
4.2 yield 和 yield from 在处理大数据量的应用
如果处理的数据量很大,可能会导致内存不足,使得程序崩溃。使用 yield 和 yield from 可以避免这种情况的发生,将数据分成 small pieces,逐步迭代处理,从而实现节约内存和提高程序效率的目的。 下面是一个处理大数据量的示例代码:
import os
path = '/path/to/your/large/dataset'
def read_large_file(path):
with open(path, 'r') as f:
while True:
data = f.read(1024)
if not data:
break
yield data
# 使用 yield from 逐个处理大文件
def process_large_file(path):
for piece in read_large_file(path):
yield from process_piece(piece)
def process_piece(piece):
# do something with piece of the data
pass
for processed_piece in process_large_file(path):
pass
运行上述代码可以将大文件逐个处理,从而避免了同时读取整个文件的情况。
5. 总结
通过本文的介绍,我们可以了解到:yield 和 yield from 都是 Python 中很重要的语句,它们可以用于实现迭代遍历数据,节约大量的内存,提高程序的效率,在协程和大数据量的处理中也有广泛的应用。但是,它们之间有着使用方式、运行效率和应用场景的差异,我们在使用的时候需要根据实际需求进行选择。