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链接。


当前回答

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

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

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

其他回答

一个容易理解它是什么的简单例子:产量

def f123():
    for _ in range(4):
        yield 1
        yield 2


for i in f123():
    print (i)

产出是:

1 2 1 2 1 2 1 2

产出关键字简化为两个简单的事实:

如果编译者在函数内的任何地方检测到产出关键字,则该关键字函数不再通过返回语句返回。相反,它会立即返回一个称为生成器的懒惰的“待决列表”对象。“生成器”是可循环的。什么是可循环的?它像列表或设置或范围或编辑视图一样,带有按一定顺序访问每个元素的内置协议。

简言之: 最常见的情况是, 发电机是一个懒惰的、 递增的等待列表, 并且产出语句允许您使用函数符号来编程生成器应该逐渐吐出的列表值。 此外, 高级用法允许您使用发电机作为共程( 见下文 ) 。

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

基本上, 只要遇到产出语句, 函数就会暂停并保存状态, 然后根据 Python 传动协议( 在某些合成结构中, 类似反复呼叫下一个( ) 的循环, 并捕捉一个停止作用的例外等) , 发出“ 列表中的下一个返回值 ” 。 您可能遇到过带有生成表达式的生成器; 生成函数更强大, 因为您可以将参数反馈到暂停的生成器功能中, 使用它们来实施 comutines 。 稍后会更多 。


基本示例(“清单”)

我们来定义一个函数,它就像 Python 的射程。 调用 makeRange(n) returns a Generator:

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(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]

要对发电机有更好的感觉,您可以玩过工具模块( 一定要使用链。 来自_ irightable, 而不是当必要时使用链子 ) 。 例如, 您甚至可以使用生成器来实施无限长的懒惰列表, 比如 ltertools. counts () 。 您可以执行您自己的除法列表( 可认证的) : zip( 计数 ) , 或使用一段时间内生成关键字来这样做 。

请注意: 发电机实际上可以用于更多的事情, 比如实施 comotines 或非 确定性编程或其他优雅的东西。 然而, 我在此介绍的“ 懒惰列表” 观点是您最常用的 。


幕后幕后

这就是“ Python 迭代协议” 的原理。 也就是说, 当您做列表( 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__ () 函数, 这是“ 电路协议” 的一部分, 并在所有迭代器中找到 。 您可以手动使用下一个( ) 函数( 和迭代协议的其他部分) 来执行奇特的东西, 通常以降低可读性为代价, 所以尽量避免这样做...


锥体

圆柱形示例:

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)

comotine (generations 通常接受通过 产出 关键字输入输入 , 例如, 下一个 Input = 产生下一个输出, 作为一种双向通信形式) 基本上是允许暂停自己和请求输入的计算( 例如它下一步应该做什么 ) 。 当 comotine 暂停自己( 当运行中的 comotine 最终会点击 产出 关键字时) , 计算会暂停, 控制会被倒回“ 调” 函数( 要求下一个计算值的框架 ) 。 暂停的发电机/ 库外 仍然暂停, 直到另一个函数( 可能是一个不同的函数/ 文本) 启用后, 请求下一个值( 通常通过输入数据将暂停的逻辑内部输入到 comutine 的代码 ) 。

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


贫提亚e

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

在 Python-speak 中, 迭代是“ 理解“ 循环概念” 的任何物体, 如列表[ 1, 2, 3] , 转动器是请求循环的具体实例, 如 [ 1, 2, 3,. . _ eter __ () 。 生成器与任何迭代器完全相同, 但它的写法除外( 带有函数语法 ) 。

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

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

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

... 然后记住发电机是一个迭代器, 也就是说, 它是一次性使用。 如果您想要再使用它, 您应该再次调用 MyRange (...) 。 如果您需要使用结果两次, 将结果转换为列表, 并存储在变量 x = 列表( MyRange (5)) 中。 那些绝对需要克隆生成器的人( 例如, 那些正在做可怕的黑客化元程序设计的人) 可以使用它的工具.tee( 仍然在 Python 3 中工作) , 如果绝对需要的话, 因为可复制的迭代器 Python PEP 标准建议已被推迟 。

简单使用实例 :

>>> def foo():
    yield 100
    yield 20
    yield 3

    
>>> for i in foo(): print(i)

100
20
3
>>> 

如何运行 : 调用时, 函数会立即返回对象。 对象可以传递到下一个( ) 函数 。 当调用下一个( ) 函数时, 您的函数会一直运行到下一个产值, 并为下一个( ) 函数提供返回值 。

在引擎盖下, 循环确认对象是一个生成对象, 并使用下一个( ) 来获取下一个值 。

在一些语言中,比如ES6和更高语言中,它的实施略有不同, 所以下一个是生成对象的成员函数, 每次它得到下一个值时, 你就可以从调用器中传递数值。 所以如果结果是生成器, 那么你可以做类似y=结果。 ext( 555) , 而程序生成值可以说像 z = 产值 999 。 y 的值将是 999 , 下一个产值是 999, 而 z 的值将是 555 , 下一个产值是 555。 Python 获取并发送方法也有类似的效果 。

虽然很多答案都表明你为什么用一个产量来创建发电机, 但对于产量来说却有更多的用途。 做一个共同的习惯很容易, 这样可以让信息在两个代码区块之间传递。 我不再重复任何已经给出的关于使用产量来创建发电机的好例子 。

为了帮助理解下一个代码中的产值, 您可以用手指通过任何有产值的代码来追踪循环。 每次你的手指碰到产量的代码时, 您必须等待下一个或发送输入。 调用下一个代码时, 您可以跟踪代码, 直到你到达产量... 产值右侧的代码被评估并返回到调用者... 然后您等待。 当调用下一个代码时, 您会通过代码执行另一个循环。 但是, 您会注意到, 产值也可以用一个发送方式来使用。 发送时, 将会将一个调用器的值发送到产值中 。 如果发送了, 则会收到发送的值, 然后将其从左侧吐出... , 然后通过代码的跟踪直到您再次到达产量( 返回最后的值, 如下一个调用 ) 。

例如:

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

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

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"