Python yield 使用浅析

xiaoxiao2021-02-28  55

要了解python yield的作用,你必须先明白什么是生成器,在此之前,你需要了解什么是可迭代对象(可迭代序列)

迭代

你可以创建一个列表,然后逐一遍历,这就是迭代 任何你可用 “for… in…” 处理的都是可迭代对象:列表,字符串,文件….

这些迭代对象非常便捷,因为你可以尽可能多地获取你想要的东西 但,当你有大量数据并把所有值放到内存时,这种处理方式可能不总是你想要的 (but you store all the values in memory and it’s not always what you want when you have a lot of values.)

dict对象以及任何实现了iter()或者getitem()方法的类都是可迭代对象,此外,可迭代对象还可以用在zip,map等函数中,当一个可迭代对象作为参数传递给内建函数iter()时,它会返回一个迭代器对象。通常没必要自己来处理迭代器本身或者手动调用iter(),for语句会自动调用iter(),它会创建一个临时的未命名的变量来持有这个迭代器用于循环期间。 为了更好的理解yield,迭代器也是很重要的。

迭代器(iterator)

迭代器代表一个数据流对象,不断重复调用迭代器的next()方法可以逐次地返回数据流中的每一项,当没有更多数据可用时,next()方法会抛出异常StopIteration。此时迭代器对象已经枯竭了,之后调用next()方法都会抛出异常StopIteration。迭代器需要有一个iter()方法用来返回迭代器本身。因此它也是一个可迭代的对象。

什么是生成器

生成器也是一个迭代器,但是你只可以迭代他们一次,不能重复迭代,因为它并没有把所有值存储在内存中,而是实时地生成值

生成器是一个特殊的程序,可以被用作控制循环的迭代行为 生成器类似于返回值为数组的一个函数,这个函数可以接收参数,可以被调用,但是,不同于一般的函数会一次性返回包含了所有数值的数组,生成器一次只产生一个值,这样消耗的内存数量大大减少,而且允许调用函数可以很快的开始处理前几个返回值。因此,生成器看起来像一个函数但是表现的却像一个迭代器。

python中的生成器 python提供了两种基本的方式。

生成器函数:也是用def来定义,利用关键字yield一次返回一个结果,阻塞,重新开始 生成器表达式:返回一个对象,这个对象只有在需要的时候才产生结果

接下来我们通过一个小程序来一步步解析 yield 的概念

yeild分步解析

求斐波那契數列 1.解析第一步,简单编个程

运行结果: 结果是没问题的,但直接在 fab 函数中用 print 打印数字会导致该函数可复用性较差,要提高 fab 函数的可复用性,我们下一步将使得此函数返回一个 List。

2.提高函数复用性 虽然函数返回 lst 能满足复用性的要求,但是该函数在运行中占用的内存会随着参数 num 的增大而增大,如果要控制内存占用,最好不要用 List 来保存中间结果,而是通过 iterable 对象来迭代

3.利用 iterable 把 fab 函数改写为一个支持 iterable 的类 然而,使用 class 改写的这个版本,代码远远没有第一版的 fab 函数来得简洁。如果我们想要保持第一版 fab 函数的简洁性,同时又要获得 iterable 的效果,yield 就派上用场了

4.使用 yield 的fab()函数 注意,在python 3.x中, next()应当替换为next() 下图为Python2.X中的运行以及结果 简单地讲,yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。

由此我们可以知道:

一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。

return 的作用

使用yield进行文件的逐块读取 在一个 generator function 中,如果没有 return,则默认执行至函数完毕,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。 另一个例子

另一个 yield 的例子来源于文件读取。如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取:

def read_file(fpath): BLOCK_SIZE = 10 with open(fpath, 'rb') as f: while True: block = f.read(BLOCK_SIZE) if block: yield block else: return

转载请注明原文地址: https://www.6miu.com/read-45863.html

最新回复(0)