Python 中产出关键字的用法是什么? 它能做什么?

例如,我试图理解这个代码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_camedates 被调用时会怎样? 列表是否返回? 单一个元素吗? 是否再次调用? 以后的电话何时停止?


1. 本代码由Jochen Schulz(jrschulz)编写,他为公制空间制作了一个伟大的Python图书馆,与完整的源:模块mspace链接。


当前回答

这里所有的答案都很好,但其中只有一个(最受投票支持的)与你的代码如何运作有关。其他的与一般的发电机有关,也与它们如何运作有关。

所以,我不重复发电机是什么或产量是什么;我认为这些都包含在现有的答案中。然而,在花了几个小时试图理解一个与你的代码相似的代码之后,我将打破它是如何运作的。

您的代码绕过二进制树结构。 让我们以这棵树为例:

    5
   / \
  3   6
 / \   \
1   4   8

另一个简单的二进制搜索树的十字路口:

class Node(object):
..
def __iter__(self):
    if self.has_left_child():
        for child in self.left:
            yield child

    yield self.val

    if self.has_right_child():
        for child in self.right:
            yield child

执行代码在树形对象上,它执行__iter___这样:

def __iter__(self):

    class EmptyIter():
        def next(self):
            raise StopIteration

    if self.root:
        return self.root.__iter__()
    return EmptyIter()

候选人发言可用树上元素替换; Python 翻译为

it = iter(TreeObj)  # returns iter(self.root) which calls self.root.__iter__()
for element in it: 
    .. process element .. 

因为节点. _ iter_ 函数是一个生成器, 内部的代码按迭代执行 。 所以执行会是这样的 :

根元素是第一个; 检查它是否留下了孩子, 并且要循环它们( 因为我们叫它它 1 ) 。 它有一个孩子, 所以执行它。 给孩子自己。 左左为自己创建一个新的循环器 。 左是节点对象本身( it2) 。 左是同一逻辑 2 , 新的循环器已经创建( it3) 。 现在我们到达了树的左端 。 现在我们到达了树的左端。 它3 没有留下孩子, 所以它会继续下去并产生自我。 在下一个呼叫( it3) 时, 它会提高停止作用, 因为它没有正确的孩子( 到达函数的尽头, 但没有产生任何效果) 。 它1 和它2 仍然在活动 - 它们没有耗尽, 调用下一个( it2) 将产生值, 而不是提高停止作用 。 现在我们回到了它的上下文 2 , 并调下一个( it2) 继续它停止它的地方 : 在产生子声明之后 。 由于它没有更多的剩余孩子, 它会继续持续并产生自我 val 。 val 。

这里的渔获是,每次迭代都会产生次标准来绕过树,并保持当前迭代的状态。 一旦它到达终点,它就会绕过堆叠,并按正确的顺序返回值( 最小的收益值首先 ) 。

您的代码示例在一种不同的技术中做了类似的事情: 它为每个孩子输入了一个元素列表, 然后在下一个迭代中, 它弹出它, 并在当前对象上运行函数代码( 也就是自定义 ) 。

我希望这对这个传奇话题有一点帮助,我花了好几个小时来画这个过程来理解它。

其他回答

这里所有的答案都很好,但其中只有一个(最受投票支持的)与你的代码如何运作有关。其他的与一般的发电机有关,也与它们如何运作有关。

所以,我不重复发电机是什么或产量是什么;我认为这些都包含在现有的答案中。然而,在花了几个小时试图理解一个与你的代码相似的代码之后,我将打破它是如何运作的。

您的代码绕过二进制树结构。 让我们以这棵树为例:

    5
   / \
  3   6
 / \   \
1   4   8

另一个简单的二进制搜索树的十字路口:

class Node(object):
..
def __iter__(self):
    if self.has_left_child():
        for child in self.left:
            yield child

    yield self.val

    if self.has_right_child():
        for child in self.right:
            yield child

执行代码在树形对象上,它执行__iter___这样:

def __iter__(self):

    class EmptyIter():
        def next(self):
            raise StopIteration

    if self.root:
        return self.root.__iter__()
    return EmptyIter()

候选人发言可用树上元素替换; Python 翻译为

it = iter(TreeObj)  # returns iter(self.root) which calls self.root.__iter__()
for element in it: 
    .. process element .. 

因为节点. _ iter_ 函数是一个生成器, 内部的代码按迭代执行 。 所以执行会是这样的 :

根元素是第一个; 检查它是否留下了孩子, 并且要循环它们( 因为我们叫它它 1 ) 。 它有一个孩子, 所以执行它。 给孩子自己。 左左为自己创建一个新的循环器 。 左是节点对象本身( it2) 。 左是同一逻辑 2 , 新的循环器已经创建( it3) 。 现在我们到达了树的左端 。 现在我们到达了树的左端。 它3 没有留下孩子, 所以它会继续下去并产生自我。 在下一个呼叫( it3) 时, 它会提高停止作用, 因为它没有正确的孩子( 到达函数的尽头, 但没有产生任何效果) 。 它1 和它2 仍然在活动 - 它们没有耗尽, 调用下一个( it2) 将产生值, 而不是提高停止作用 。 现在我们回到了它的上下文 2 , 并调下一个( it2) 继续它停止它的地方 : 在产生子声明之后 。 由于它没有更多的剩余孩子, 它会继续持续并产生自我 val 。 val 。

这里的渔获是,每次迭代都会产生次标准来绕过树,并保持当前迭代的状态。 一旦它到达终点,它就会绕过堆叠,并按正确的顺序返回值( 最小的收益值首先 ) 。

您的代码示例在一种不同的技术中做了类似的事情: 它为每个孩子输入了一个元素列表, 然后在下一个迭代中, 它弹出它, 并在当前对象上运行函数代码( 也就是自定义 ) 。

我希望这对这个传奇话题有一点帮助,我花了好几个小时来画这个过程来理解它。

以下是一些Python的例子, 说明如何实际安装发电机, 仿佛Python没有提供同声糖:

作为Python发电机:

from itertools import islice

def fib_gen():
    a, b = 1, 1
    while True:
        yield a
        a, b = b, a + b

assert [1, 1, 2, 3, 5] == list(islice(fib_gen(), 5))

使用地法关闭代替发电机

def ftake(fnext, last):
    return [fnext() for _ in xrange(last)]

def fib_gen2():
    #funky scope due to python2.x workaround
    #for python 3.x use nonlocal
    def _():
        _.a, _.b = _.b, _.a + _.b
        return _.a
    _.a, _.b = 0, 1
    return _

assert [1,1,2,3,5] == ftake(fib_gen2(), 5)

使用关闭物体而不是发电机(因为关闭物体和物体是等效的)

class fib_gen3:
    def __init__(self):
        self.a, self.b = 1, 1

    def __call__(self):
        r = self.a
        self.a, self.b = self.b, self.a + self.b
        return r

assert [1,1,2,3,5] == ftake(fib_gen3(), 5)

产出关键字只收集返回结果。将产出想象成返回 {{}}} 。

和每个答案一样, 收益被用于创建序列生成器。 它用于动态生成某些序列。 例如, 在按行阅读网络文件行时, 您可以使用以下的收益函数 :

def getNextLines():
   while con.isOpen():
       yield con.read()

您可在您的代码中使用以下代码:

for line in getNextLines():
    doSomeThing(line)

执行控制控制

执行控制将会从 GetNextLines () 转到执行时的循环。 因此, 每次引用 NextLines () 时, 执行都会从上次暂停处开始 。

因此,简言之,一个函数具有以下代码

def simpleYield():
    yield "first time"
    yield "second time"
    yield "third time"
    yield "Now some useful value {}".format(12)

for i in simpleYield():
    print i

将打印

"first time"
"second time"
"third time"
"Now some useful value 12"

我本打算张贴“Beazley的“Python:基本参考”第19页,

另外,请注意, 产量可以作为发电机功能中的双重用途, 共程中可以使用。 虽然这与您的代码片断不相同, 但( ield) 也可以用作函数中的表达方式。 当调用者使用发送( ) 方法给方法发送一个值时, 共程将执行到遇到下一个( yeld) 语句时 。

生成器和共同路由是建立数据流类型应用程序的很酷的方法。 我认为值得知道在函数中产出语句的另一种用途。