版权声明:此文章转载自
如需转载请联系听云College团队成员小尹 邮箱:yinhy#tingyun.com
在介绍 yield 之前有必要先说明下 Python 中的迭代器( iterator)和生成器( generator)
在 Python 中, for 循环可以用于 Python 中的任何类型,包括列表、元组等等,实际上 for 循环可用于任
何“可迭代对象”,这其实就是迭代器。
迭代器是一个实现了迭代器协议的对象, Python 中的迭代协器协议就是用 next 方法的对象前进到下一个
结果,而在一系列结果的末尾,则会引发 StopIteration。任何这类的对象在 Python 中都可以用 for 循环或
其他遍历工具迭代, 迭代工具内部会在每次迭代时调用 next 方法,并且捕获 StopIteration 异常来确定何
时离开。
使用迭代器一个显而易见的好处就是: 每次只从对象中获取一条数据,不会造成内存的过大开销。
比如要逐行读取一个文件的内容,利用 readlines()方法,可以这样写:
for line in open('test.txt').readlines(): print(line)
这样的代码虽然可以工作, 但不是最好的方法。因为它实际上是把文件一次加载到内容中,然后逐行打
印,当文件很大时,这个方法的内容开销就很大了。
利用 file 的迭代器,可以这样写:
# use file iterators for line in open('test.txt'): print(line)
它并没有显示的去读取文件, 而是利用迭代器每次读取下一行
生成器函数在 Python 中与迭代器协议的概念联系在一起,简而言之,包含 yield 语句的函数会被编译成
生成器。当函数被调用时返回一个生成器对象, 这个对象支持迭代器接口。
通过列表生成式,可以直接创建一个列表, 如:
>>> L = [x * x for x in range(10)] >>> L [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
但是, 受到内存限制, 列表容量肯定是有限的, 而且创建一个包含 100 万个元素的列表,不仅占用很大
的存储空间,而且如果仅仅需要访问前面几个元素的时候,后面绝大多数元素占用的空间就白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那是否可以在循环的过程中不断推算出后续的元素呢?
这样就不必创建完成的 list, 从而节省大量空间, 在 Python 中这种一边循环一边计算的机制,称为生成
器( generator)。
生成器不像一半的函数会生成值后退出,生成器函数在生成值后会自动挂起并暂停他们的执行和状态,
它的本地变量保存状态信息, 这些信息在函数恢复时将再度有效
def g(n): for i in range(n): yield i**2 for i in g(5): print(i, end=':') 输出: 0:1:4:9:16:
要了解它的运行原理,用 next 方法看看:
>>> t = g(5) >>> next(t) 0 >>> next(t) 1 >>> next(t) 4 >>> next(t) 9 >>> next(t) 16 >>> next(t) Traceback (most recent call last): File “”, line 1, in StopIteration
在运行完 5 次 next 之后,生成器抛出一个 StopIteration 异常,迭代终止。
再来看一个 yield 的例子,用生成器生成一个斐波那契数列
def fab(max): a, b = 0, 1 while a < max: yield a a, b = b, a+b for i in fab(20): print(i, end=',')
看到这里应该就能理解生成器那个很抽象的概念了吧~~
这里,最难理解的就是生成器和函数的执行流程不一样, 函数是顺序执行的,遇到 return 语句或者最后
一行函数语句就返回, 而生成器函数, 在每次调用 next()的时候执行,遇到 yield 语句返回, 再次执行时
从上次返回的 yield 语句处继续执行。
简单看个例子, 定义一个 generator,依次返回 1, 3, 5
def odd(): print('step 1') yield 1 print('step 2') yield 3 print('step 3') yield 5 >>> o = odd() >>> next(o) step 1 1 >>> next(o) step 2 3 >>> next(o) Step 3 5 >>> next(o) Traceback (most recent call last): File “”, line 1, in StopIteration
可以看到, odd 不是普通函数,而是 generator,在执行过程中,遇到 yield 就中断,下次又继续执行。执
行 3 次 yield 后,已经没有 yield 可以执行了,所以,第 4 次调用 next(o)就报错。
作者:头条号 / 麦穗技术
链接:http://toutiao.com/i6291401456868131329/
来源:头条号(今日头条旗下创作平台)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
想阅读更多技术文章,请访问听云技术博客,访问听云官方网站感受更多应用性能优化魔力。