Python中的 yield
关键字有什么用?它有什么作用?
例如,我试图理解这段代码1 :
def _get_child_candidates(self, distance, min_dist, max_dist):
if self._leftchild and distance - max_dist < self._median:
yield self._leftchild
if self._rightchild and distance + max_dist >= self._median:
yield self._rightchild
这是调用者:
result, candidates = [], [self]
while candidates:
node = candidates.pop()
distance = node._get_dist(obj)
if distance <= max_dist and distance >= min_dist:
result.extend(node._values)
candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result
调用方法 _get_child_candidates
时会发生什么?是否返回列表?单一元素?又叫了吗?后续调用何时停止?
1. 这段代码由 Jochen Schulz (jrschulz) 编写,他为度量空间制作了一个很棒的 Python 库。这是完整源代码的链接: Module mspace 。
原文由 Alex. S. 发布,翻译遵循 CC BY-SA 4.0 许可协议
要了解
yield
做了什么,您必须了解 生成器 是什么。在你了解生成器之前,你必须了解 iterables 。可迭代对象
创建列表时,您可以一一阅读其项目。一项一项地读取它的项目称为迭代:
mylist
是一个 可迭代 的 .当您使用列表推导时,您会创建一个列表,因此是一个可迭代的:您可以使用“
for... in...
”的所有内容都是可迭代的;lists
,strings
, 文件…这些可迭代对象很方便,因为您可以随心所欲地读取它们,但是您将所有值存储在内存中,当您有很多值时,这并不总是您想要的。
发电机
生成器是迭代器,一种 只能迭代一次 的可迭代对象。生成器不会将所有值存储在内存中, 它们会即时生成值:
除了您使用
()
而不是[]
之外,它是一样的。但是,您 不能 第二次执行for i in mygenerator
因为生成器只能使用一次:它们计算 0,然后忘记它并计算 1,然后一个接一个地结束计算 4。屈服
yield
是一个类似于return
的关键字,除了该函数将返回一个生成器。这是一个无用的示例,但是当您知道您的函数将返回大量值而您只需要读取一次时,它会很方便。
要掌握
yield
,你必须明白, 当你调用函数时,你写在函数体中的代码是不会运行的。 该函数只返回生成器对象,这有点棘手。然后,您的代码将在每次
for
使用生成器时从中断处继续。现在最困难的部分:
第一次
for
调用从您的函数创建的生成器对象,它将从头开始运行您的函数中的代码,直到它命中yield
,然后它将返回第一个值的循环。然后,每个后续调用将运行您在函数中编写的循环的另一次迭代并返回下一个值。这将一直持续到生成器被认为是空的,这发生在函数运行时没有命中yield
。这可能是因为循环已经结束,或者因为您不再满足"if/else"
。你的代码解释
发电机:
呼叫者:
此代码包含几个智能部分:
循环在一个列表上进行迭代,但在迭代循环时列表会扩展。这是一种遍历所有这些嵌套数据的简洁方法,即使它有点危险,因为您最终可能会陷入无限循环。在这种情况下,
candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
耗尽了生成器的所有值,但是while
不断创建新的生成器对象,这些生成器对象将产生与以前的值不同的值,因为它没有应用于同一节点。extend()
方法是一个列表对象方法,它需要一个可迭代对象并将其值添加到列表中。通常我们传递一个列表给它:
但是在您的代码中,它有一个生成器,这很好,因为:
它之所以有效,是因为 Python 不关心方法的参数是否为列表。 Python 需要可迭代对象,因此它可以处理字符串、列表、元组和生成器!这被称为鸭子类型,这也是 Python 如此酷的原因之一。但这是另一个故事,另一个问题……
你可以在这里停下来,或者阅读一下以了解生成器的高级用法:
控制发电机耗尽
注意: 对于 Python 3,请使用
print(corner_street_atm.__next__())
或print(next(corner_street_atm))
它可以用于控制对资源的访问等各种事情。
Itertools,你最好的朋友
itertools 模块包含操作可迭代对象的特殊函数。曾经想复制一个生成器吗?链接两个发电机?使用单线对嵌套列表中的值进行分组?
Map / Zip
不创建另一个列表?然后只是
import itertools
。一个例子?让我们看看四马比赛的可能到达顺序:
了解迭代的内在机制
迭代是一个暗示可迭代(实现
__iter__()
方法)和迭代器(实现__next__()
方法)的过程。可迭代对象是您可以从中获取迭代器的任何对象。迭代器是允许您迭代可迭代对象的对象。在这篇文章中有更多关于
for
循环如何工作 的内容。