初学 Python 的开发者经常会发现很多 Python 函数中用到了 yield 关键字,然而,带有 yield 的函数执行流程却和普通函数不一样,yield 到底用来做什么,为什么要设计 yield ?本文将由浅入深地讲解 yield 的概念和用法,帮助读者体会 Python 里 yield 简单而强大的功能。
您可能听说过,带有 yield 的函数在 Python 中被称之为 generator(生成器),何谓 generator ?
我们先抛开 generator,以一个常见的编程题目来展示 yield 的概念。
如何生成斐波那契數列
斐波那契(Fibonacci)數列是一个非常简单的递归数列,除第一个和第二个数外,任意一个数都可由前两个数相加得到。用计算机程序输出斐波那契數列的前 N 个数是一个非常简单的问题,许多初学者都可以轻易写出如下函数:
清单 1. 简单输出斐波那契數列前 N 个数
def fab(max): n, a, b = 0, 0, 1 while n < max: print b a, b = b, a + b n = n + 1
>>> fab(5) 1 1 2 3 5
def fab(max): n, a, b = 0, 0, 1 L = [] while n < max: L.append(b) a, b = b, a + b n = n + 1 return L
>>> for n in fab(5): ... print n ... 1 1 2 3 5
清单 3. 通过 iterable 对象来迭代
for i in range(1000): pass
for i in xrange(1000): pass
class Fab(object):def __init__(self, max): self.max = max self.n, self.a, self.b = 0, 0, 1
def __iter__(self): return self
def next(self): if self.n < self.max: r = self.b self.a, self.b = self.b, self.a + self.b self.n = self.n + 1 return r raise StopIteration()
>>> for n in Fab(5): ... print n ... 1 1 2 3 5
清单 5. 使用 yield 的第四版
def fab(max): n, a, b = 0, 0, 1 while n < max: yield b # print b a, b = b, a + b n = n + 1'''
>>> for n in fab(5): ... print n ... 1 1 2 3 5
清单 6. 执行流程
>>> f = fab(5) >>> f.next() 1 >>> f.next() 1 >>> f.next() 2 >>> f.next() 3 >>> f.next() 5 >>> f.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
当函数执行结束时,generator 自动抛出 StopIteration 异常,表示迭代完成。在 for 循环里,无需处理 StopIteration 异常,循环会正常结束。
我们可以得出以下结论:
一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。
如何判断一个函数是否是一个特殊的 generator 函数?可以利用 isgeneratorfunction 判断:
清单 7. 使用 isgeneratorfunction 判断
>>> from inspect import isgeneratorfunction >>> isgeneratorfunction(fab) True
清单 8. 类的定义和类的实例
>>> import types >>> isinstance(fab, types.GeneratorType) False >>> isinstance(fab(5), types.GeneratorType) True
>>> from collections import Iterable >>> isinstance(fab, Iterable) False >>> isinstance(fab(5), Iterable) True
每次调用 fab 函数都会生成一个新的 generator 实例,各实例互不影响:
>>> f1 = fab(3) >>> f2 = fab(5) >>> print 'f1:', f1.next() f1: 1 >>> print 'f2:', f2.next() f2: 1 >>> print 'f1:', f1.next() f1: 1 >>> print 'f2:', f2.next() f2: 1 >>> print 'f1:', f1.next() f1: 2 >>> print 'f2:', f2.next() f2: 2 >>> print 'f2:', f2.next() f2: 3 >>> print 'f2:', f2.next() f2: 5
return 的作用
在一个 generator function 中,如果没有 return,则默认执行至函数完毕,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。
另一个例子
另一个 yield 的例子来源于文件读取。如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取:
清单 9. 另一个 yield 的例子
def read_file(fpath): BLOCK_SIZE = 1024 with open(fpath, 'rb') as f: while True: block = f.read(BLOCK_SIZE) if block: yield block else: return
本文向大家介绍浅析PyTorch中nn.Linear的使用,包括了浅析PyTorch中nn.Linear的使用的使用技巧和注意事项,需要的朋友参考一下 查看源码 Linear 的初始化部分: 需要实现的内容: 计算步骤: 返回的是:input * weight + bias 对于 weight 对于 bias 实例展示 举个例子: 张量的大小由 140 x 100 变成了 140 x 50 执行的
本文向大家介绍浅析Node.js 中 Stream API 的使用,包括了浅析Node.js 中 Stream API 的使用的使用技巧和注意事项,需要的朋友参考一下 本文由浅入深给大家介绍node.js stream api,具体详情请看下文吧。 基本介绍 在 Node.js 中,读取文件的方式有两种,一种是用 fs.readFile ,另外一种是利用 fs.createReadStream 来
本文向大家介绍浅析BootStrap Treeview的简单使用,包括了浅析BootStrap Treeview的简单使用的使用技巧和注意事项,需要的朋友参考一下 bootstrap-treeview.js1是一款强大的树菜单插件,本文给大家介绍bootstrap treeview的简单使用。 废话不多说,直接上干干货。 1、bootstrap-treeview Github网址: https:/
本文向大家介绍Python中optparse模块使用浅析,包括了Python中optparse模块使用浅析的使用技巧和注意事项,需要的朋友参考一下 最近遇到一个问题,是指定参数来运行某个特定的进程,这很类似Linux中一些命令的参数了,比如ls -a,为什么加上-a选项会响应。optparse模块实现的也是类似的功能,它是为脚本传递命令参数。 使用此模块前,首先需要导入模块中的类OptionPar
本文向大家介绍浅析Java中JSONObject和JSONArray使用,包括了浅析Java中JSONObject和JSONArray使用的使用技巧和注意事项,需要的朋友参考一下 废话不多说,先给大家贴代码,具体代码如下所示: 运行结果如下: 通过构造器的方式创建的JSONObject对象:{"derek":"23","dad":"49","mom":"45"} 通过fromObject方法将ma
本文向大家介绍浅析使用Python操作文件,包括了浅析使用Python操作文件的使用技巧和注意事项,需要的朋友参考一下 1. file=open('xxx.txt', encoding='utf-8'),open()函数是Python内置的用于对文件的读写操作,返回的是文件的流对象(而不是文件本身,所以使用的方法都是流对象的方法)。使用这个函数时推荐指定encoding参数(Python2.7.x