何为使用yieldPython 中的关键字?

比如说,我在试着理解这个代码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图书馆。模块 m 空间.

当前回答

所有的答案都是伟大的, 但对于新人来说有点困难。

我猜你已经学会了return语句。

作为类比,returnyield双胞胎。return意指“返回和停止”,而“真正”意指“返回,但继续”

  1. 尝试获得 num_ list 列表return.
def num_list(n):
    for i in range(n):
        return i

运行它:

In [5]: num_list(3)
Out[5]: 0

你看,你只得到一个数字 而不是他们的名单。return永远不允许你快乐地胜利, 仅仅一次执行,然后退出。

  1. 来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来来yield

替换returnyield:

In [10]: def num_list(n):
    ...:     for i in range(n):
    ...:         yield i
    ...:

In [11]: num_list(3)
Out[11]: <generator object num_list at 0x10327c990>

In [12]: list(num_list(3))
Out[12]: [0, 1, 2]

现在,你赢得了所有的数字。

return运行一次,停止一次,yield计划运行时间。您可以解释return计为return one of them, 和yield计为return all of them。这被称为iterable.

  1. 再来一步,我们可以改写yield声明的语中return
In [15]: def num_list(n):
    ...:     result = []
    ...:     for i in range(n):
    ...:         result.append(i)
    ...:     return result

In [16]: num_list(3)
Out[16]: [0, 1, 2]

这是核心yield.

列表之间的差别return输出和对象yield输出为 :

您将总是从列表对象中获取 [0, 1, 2] 列表对象, 但只能从“ 对象” 中获取它们yield输出一次。 所以, 它有一个新名称generator对象显示于Out[11]: <generator object num_list at 0x10327c990>.

最后,作为格罗克语的比喻:

  • returnyield双胞胎
  • listgenerator双胞胎

其他回答

还有一件事情要提: 产量的函数其实不一定要终止。我写了这样的代码:

def fib():
    last, cur = 0, 1
    while True: 
        yield cur
        last, cur = cur, last + cur

这样我就可以用在别的代码里了

for f in fib():
    if some_condition: break
    coolfuncs(f);

它确实有助于简化一些问题,使一些事情更容易处理。

缩略yield关键字缩写为两个简单的事实:

  1. 如果汇编者检测到yield关键字任何地方函数内部的函数,该函数不再通过return语句。取代, 它, 它立即立即返回返回 a“等待列表”对象调用发电机
  2. 发电机是易用的,什么是易 易 易 性它的任何东西 像一个listsetrange或 dict-view, 带有按一定顺序视察每个要素的内建程序规程.

概括地说:最常见的情况是,a 发电机是一个懒惰、递增的待用清单。, 和yield语句允许您使用函数符号来编程列表值发电机应该逐渐吐出来此外,先进用途使你能够使用发电机作为共同路线(见下文)。

generator = myYieldingFunction(...)  # basically a list (but lazy)
x = list(generator)  # evaluate every element into a list

   generator
       v
[x[0], ..., ???]

         generator
             v
[x[0], x[1], ..., ???]

               generator
                   v
[x[0], x[1], x[2], ..., ???]

                       StopIteration exception
[x[0], x[1], x[2]]     done

基本上,当yield语句被遇到,函数暂停并保存状态,然后根据 python 传动协议发布“ 列表中下一个返回值” 。next()并捕获aStopIteration您可能遇到过发电机,例如:发电机表达式; 发电机功能更强大,因为您可以将参数反馈到暂停的发电机功能中,用它们来实施共同路线。稍后更多。


基本示例(“清单”)

让我们定义一个函数makeRange和皮松的一模一样range调用makeRange(n)将一个天才:

def makeRange(n):
    # return 0,1,2,...,n-1
    i = 0
    while i < n:
        yield i
        i += 1

>>> makeRange(5)
<generator object makeRange at 0x19e4aa0>

要迫使发电机立即返回其待处理值, 您可以将它传送到list()(就像你可以 任何可重复的):

>>> list(makeRange(5))
[0, 1, 2, 3, 4]

比较“仅返回列表”的示例

上述例子可视为仅仅是创建一份清单,并附在后面并返回:

# return a list                  #  # return a generator
def makeRange(n):                #  def makeRange(n):
    """return [0,1,2,...,n-1]""" #      """return 0,1,2,...,n-1"""
    TO_RETURN = []               # 
    i = 0                        #      i = 0
    while i < n:                 #      while i < n:
        TO_RETURN += [i]         #          yield i
        i += 1                   #          i += 1
    return TO_RETURN             # 

>>> makeRange(5)
[0, 1, 2, 3, 4]

不过,有一个重大差别;见最后一节。


您如何使用发电机

所有发电机都是易变的, 所以它们经常被这样使用:

#                  < ITERABLE >
>>> [x+10 for x in makeRange(5)]
[10, 11, 12, 13, 14]

为了对发电机有更好的感觉,你可以和发电机一起玩itertools模块 (必须使用)chain.from_iterable而不是chain例如,你甚至可能使用发电机来实施无穷无尽的懒惰清单,例如:itertools.count()您可以执行您自己的def enumerate(iterable): zip(count(), iterable),或者与yield时段循环中的关键字 。

请注意:发电机实际上可以用于更多的其他物品,例如:实施共同方案或非确定性编程或其他优雅的东西。 然而, 我在此展示的“ 懒惰列表” 观点是您最常用的 。


幕后幕后

这就是“ Python 迭代协议” 是如何工作的。 也就是说, 当您在list(makeRange(5))。这就是我刚才所说的“懒惰、递增清单”。

>>> x=iter(range(5))
>>> next(x)  # calls x.__next__(); x.next() is deprecated
0
>>> next(x)
1
>>> next(x)
2
>>> next(x)
3
>>> next(x)
4
>>> next(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

内置函数next()只需调用物体.__next__()函数,该函数是“终止协议”的一部分,并在所有迭代器中查找。您可以手动使用next()函数( 以及迭代协议的其他部分) 来实施花哨, 通常以降低可读性为代价, 所以尽量避免这样做...


锥体

锥体例如:

def interactiveProcedure():
    userResponse = yield makeQuestionWebpage()
    print('user response:', userResponse)
    yield 'success'

coroutine = interactiveProcedure()
webFormData = next(coroutine)  # same as .send(None)
userResponse = serveWebForm(webFormData)

# ...at some point later on web form submit...

successStatus = coroutine.send(userResponse)

共同常规(通常通过下列途径接受输入的发电机)yielde.g.nextInput = yield nextOutput,作为双向通信的一种形式)基本上是一种计算方法,它允许暂停自己并请求输入(例如,它下一步应该做什么)。当共程本身暂停时(当运行中的共程最终击中yield键,计算被暂停,控制被倒回“调用”功能(要求next暂停的生成器/ coutine 仍然暂停, 直到另一个引用函数( 可能是一个不同的函数/ 变量) 要求下一个值来取消它( 通常通过输入数据将暂停的逻辑内含引导到 coroutine 代码 ) 。

您可以将皮延共程视为懒惰的递增待决列表, 下一个元素不仅取决于先前的计算, 而且还取决于输入, 您可以选择在生成过程中注射 。


贫提亚e

通常,大多数人不会关心以下的区别,可能想在这里停止阅读。

在Python-speak语中,易 易 易 性中“理解“循环”概念的任意对象,如列表[1,2,3],和一个振动器是请求循环( 类似) 的具体实例[1,2,3].__iter__()A. A. A.发电机发电机与任何迭代器完全相同,但文字写法除外(用函数语法)。

当您从列表中请求一个迭代器时, 它会创建一个新的迭代器。 但是, 当您从一个迭代器中请求一个迭代器( 您很少会这样做 ) 时, 它只会给您一个副本 。

因此,在不可能的情况下,你没有 做这样的事情...

> x = myRange(5)
> list(x)
[0, 1, 2, 3, 4]
> list(x)
[]

...然后记住发电机是振动器;即,这是一次性使用。如果您想要重新使用它,您应该拨打myRange(...)如果您需要两次使用结果,将结果转换为列表并将其存储在变量中x = list(myRange(5))。那些绝对需要克隆生成器的人(例如,正在做可怕的黑化元方案化的人)可以使用itertools.tee (Python 3仍然在工作(如果绝对必要,自Python PEP 标准提案推迟审议。

虽然很多答案 表明你为什么会使用yield要创建生成器, 有更多的用途yield来传递两个代码区块之间的信息。我不会重复任何已经提供的关于使用yield创建生成器。

帮助理解什么是yield在以下代码中,您可以使用手指通过任何具有yield。 每次你的手指碰到yield你必须等待next或 a/send要输入。当next被调用,你通过代码追踪 直到你击中yield. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .yield被评价并返回到打电话者... 然后你等待。next被再次调用,您通过代码执行另一个循环。 但是,你会注意到,在一条共同的常规中,yield也可以与 a 一起使用send... 将会从调用器中发送一个值产生函数。如果send给给, 然后给yield接收发送的值,然后吐出左边的左手侧... 然后通过代码的追踪进展,直到你击中yield再次返回(在结尾处返回值,如同next也有人打电话))

例如:

>>> def coroutine():
...     i = -1
...     while True:
...         i += 1
...         val = (yield i)
...         print("Received %s" % val)
...
>>> sequence = coroutine()
>>> sequence.next()
0
>>> sequence.next()
Received None
1
>>> sequence.send('hello')
Received hello
2
>>> sequence.close()

下面是浅白语言的例子。我将提供高层次人类概念与低层次Python概念之间的对应关系。

我想用数字序列操作, 但我不想用这个序列的创建来烦恼我自己, 我只想专注于我想做的操作。 因此, 我做以下工作:

  • 我打电话给你,告诉你,我想要一个数字序列 以特定的方式计算, 我让你知道算法是什么。
    此步骤对应于def内插入发电机函数,即包含yield.
  • 稍后,我告诉你, "好了,准备好告诉我数字的顺序"。
    此步骤对应于调用发电机函数, 以返回发电机对象 。注意不要告诉我任何数字 你只要拿起你的纸和铅笔
  • 我问你,"告诉我下一个号码",然后你告诉我第一个号码, 在那之后,你等我问你下一个号码。你的工作是记住你在哪里,你已经说过什么号码,下一个号码是什么。 我不在乎细节。
    此步骤对应于调用next(generator)在发电机的物体上。
    (在Python 2,.next是产生器物体的一种方法;在Python 3中,它被命名为.__next__,但正确的称呼方式是使用内置next()函数类似len().__len__)
  • ...重复前一步,直到...
  • 最终,你可能会走到尽头。你不会告诉我一个数字;你只会喊叫,“抓住你的马!我受够了!不再有数字了!”
    此步骤对应于生成器对象结束工作, 并提升StopIteration例外。
    生成器函数不需要提出例外。 当函数结束或发布时自动生成 。return.

这就是发电机(包含yield;它开始执行第一个next()时暂停yield,当要求next()它从最后一点继续值 。 它的设计完全符合 Python 的迭代协议, 它描述了如何按顺序请求值 。

循环程序最著名的用户是for在 Python 中命令。 所以, 当您做 :

for item in sequence:

这不重要,如果sequence是列表、字符串、字典或生成器对象对象如上文所述;结果相同:您逐个阅读顺序中的项目。

请注意def函数内含有yield关键字不是创建生成器的唯一方法; 它只是创建生成器的最简单的方法 。

将 " 更准确的信息 " 改为 " 更准确的信息 "迭代器类型、、 和收益单报表发电机发电机在 Python 文档中。

在皮顿generators(一种特殊类型的iterators)用于产生一系列数值和yield关键字就和return生成功能关键字。

另一件有趣的事yield关键字在保存statea 发电机功能.

所以,我们可以设定number每次对一个不同的值generator产值。

以下是一个例子:

def getPrimes(number):
    while True:
        if isPrime(number):
            number = yield number     # a miracle occurs here
        number += 1

def printSuccessivePrimes(iterations, base=10):
    primeGenerator = getPrimes(base)
    primeGenerator.send(None)
    for power in range(iterations):
        print(primeGenerator.send(base ** power))