Python中yield的解释

翻译

来源于stackoverflow问答,原文链接 Here

SN上面看到的,顺手翻译下,第一次翻译,好多地方翻的不是很好 :)


问题:

例如,我想理解以下代码

下面是调用者

在_get_child_candidates这个函数被调用时发生了什么?返回了一个列表?还是只返回了一个元素?然后又再次被调用?什么时候调用结束?

这段代码的来源 Jochen Schulz (jrschulz), who made a great Python library for metric spaces. 完整源码链接: here


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

迭代

你可以创建一个列表,然后逐一遍历,这就是迭代

mylist是可迭代的对象,当你使用列表解析时,你创建一个列表,即一个可迭代对象

任何你可用 “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.)

生成器

生成器是迭代器,但你只能遍历它一次(iterate over them once)
因为生成器并没有将所有值放入内存中,而是实时地生成这些值

这和使用列表解析地唯一区别在于使用()替代了原来的[]

注意,你不能执行for i in mygenerator第二次,因为每个生成器只能被使用一次: 计算0,并不保留结果和状态,接着计算1,然后计算4,逐一生成

yield

yield是一个关键词,类似return, 不同之处在于,yield返回的是一个生成器

这个例子并没有什么实际作用,仅说明当你知道你的函数将产生大量仅被读取一次的数据时,使用生成器将是十分有效的做法

要掌握yield,你必须明白 – 当你调用这个函数,函数中你书写的代码并没有执行。这个函数仅仅返回一个生成器对象

这有些狡猾 :-)

然后,在每次for循环使用生成器时,都会执行你的代码

然后,是比较困难的部分:

第一次函数将会从头运行,直到遇到yield,然后将返回循环的首个值. 然后,每次调用,都会执行函数中的循环一次,返回下一个值,直到没有值可以返回

当循环结束,或者不满足”if/else”条件,导致函数运行但不命中yield关键字,此时生成器被认为是空的

问题代码的解释

生成器:

调用者:

这段代码包含几个灵活的部分:

1.这个循环遍读取历候选列表,但过程中,候选列表不断扩展:-)

这是一种遍历嵌套数据的简明方法,虽然有些危险,你或许会陷入死循环中

在这个例子中, candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) 读取了生成器产生的所有值, 同时while循环产生新的生成器对象加入到列表,因为每个对象作用在不同节点上,所以每个生成器都将生成不同的值

2.列表方法extend() 接收一个生成器,生成器的所有值被添加到列表中

通常,我们传一个列表作为参数:

但是,在代码中,这个函数接受到一个生成器

这样的做法好处是:

1.你不需要重复读这些值

2.你可能有海量的子节点,但是不希望将所有节点放入内存

并且,可以这么传递生成器作为参数的原因是,Python不关心参数是一个方法还是一个列表

Python接收可迭代对象,对于字符串,列表,元组还有生成器,都适用!

这就是所谓的“鸭子类型”(duck typing), 这也是Python如此酷的原因之一, 但这是另一个问题了,对于这个问题……

你可以在这里完成阅读,或者读一点点生成器的进阶用法:

控制一个生成器的消耗

这在很多场景都非常有用,例如控制资源的获取

Itertools

一个很好的工具

itertools模块包含很多处理可迭代对象的具体方法. 例如

复制一个生成器?连接两个生成器?一行将嵌套列表中值分组?不使用另一个列表进行Map/Zip?
(Ever wish to duplicate a generator? Chain two generators? Group values in a nested list with a one liner? Map / Zip without creating another list?)

只需要使用itertools模块

一个例子,4匹马赛跑的可能抵达顺序

了解迭代器的内部机制

迭代过程包含可迭代对象(实现__iter__()方法) 和迭代器(实现__next__()方法)

你可以获取一个迭代器的任何对象都是可迭代对象,迭代器可以让你迭代遍历一个可迭代对象(Iterators are objects that let you iterate on iterables.) [好拗口:]

更多关于这个问题的 how does the for loop work

如果你喜欢这个回答,你也许会喜欢我关于 decoratorsmetaclasses 的解释

打赏支持我写出更多好文章,谢谢!

打赏作者

打赏支持我写出更多好文章,谢谢!

任选一种支付方式

1 3 收藏 1 评论

关于作者:wklken

Pythonista/vimer 个人主页 · 我的文章 · 37 ·   

相关文章

可能感兴趣的话题



直接登录
最新评论
  • weeds Linux软件工程师 2015/12/18

    关于Bank() 类那个, 我实践中发现不一致。 我用的是python2.7, 发现只要将crisis设置为False其仍然会输出$100, 不论之前设置的是什么。不知道楼主是否测试过?

跳到底部
返回顶部