转载

Python 分享之 yield/asyncio

版权声明:此文章转载自

如需转载请联系听云College团队成员小尹 邮箱:yinhy#tingyun.com

在介绍 yield 之前有必要先说明下 Python 中的迭代器( iterator)和生成器( generator)

一、 迭代器( iterator)

在 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)

它并没有显示的去读取文件, 而是利用迭代器每次读取下一行

二、 生成器( generator)

生成器函数在 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/

来源:头条号(今日头条旗下创作平台)

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

想阅读更多技术文章,请访问听云技术博客,访问听云官方网站感受更多应用性能优化魔力。

原文  https://blog.tingyun.com/web/article/detail/675
正文到此结束
Loading...