何为使用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 空间.

当前回答

缩略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简直就像return区别在于,下次你打电话给发电机时,从最后一次呼叫开始执行。yield与返回不同的语句,当生成时, 堆叠框架不会被清理, 但是控件会被转回调用方, 所以下次调用函数时, 它的状态将会恢复 。

对于您的代码,函数get_child_candidates动作就像一个循环器,这样当您扩展列表时,它会一次向新列表添加一个元素。

list.extend在你公布的代码样本中, 只需将图普还给列表, 并附加到列表中, 就会更加清晰 。

TL; DR TR; TL; TDR

代替此:

def square_list(n):
    the_list = []                         # Replace
    for x in range(n):
        y = x * x
        the_list.append(y)                # these
    return the_list                       # lines

这样做:

def square_yield(n):
    for x in range(n):
        y = x * x
        yield y                           # with this one.

每当你发现自己从头到尾 编造了一张清单yield换成每块

这是我第一次"啊哈"节奏节奏


yield是 a 是糖糖

构建一系列材料

相同行为 :

>>> for square in square_list(4):
...     print(square)
...
0
1
4
9
>>> for square in square_yield(4):
...     print(square)
...
0
1
4
9

不同的行为 :

成绩是单行:只有一次循环才能通过。当一个函数在其中产生产量时,我们把它称为发电机功能和一个振动器也就是它所返回的。这些术语是明亮的。我们失去了一个容器的方便,但获得了一系列的能量, 而这些能量是按需要计算, 并且任意地长。

成绩是懒惰,它会推迟计算计算。当你叫它时,它不会实际执行。返回函数返回振动器对象记得它留下的痕迹 每次你打电话next()转动器上(这发生在换行)行刑的几英寸向下一产地前进。return提高停止电流并结束序列( 这是循环的自然端 ) 。

成绩是多功能性。数据不必全部储存在一起,数据可以一次提供一次。数据可以是无限的。

>>> def squares_all_of_them():
...     x = 0
...     while True:
...         yield x * x
...         x += 1
...
>>> squares = squares_all_of_them()
>>> for _ in range(4):
...     print(next(squares))
...
0
1
4
9

需要时多个通行证系列剧不会太长,只是打个电话list()以下列方式:

>>> list(square_yield(4))
[0, 1, 4, 9]

最聪明的词选yield原因原因双两个意思应用 :

收益率生产或供应(如农业)

...在系列中提供下一个数据

收益率- 放弃或放弃(与政治权力一样)

...在传动器推进之前,将CPU执行。

从方案拟订的角度来看,迭代器是按下列方式执行的:Tunks 缩图.

实施迭代机、发电机和用于同时执行的线性集合,等等,作为杜丘,一种用途发送到关闭对象的信件它有一个调度员, 和给“ 消息” 的发件人解答.

"下一个"是发送到结束处的电文,该电文由“创建者”创建。亚列"打电话。

执行此计算有很多方法。 我使用突变, 但可以通过返回当前值和下一个生成者( 生成者) 返回当前值和下一个生成者( 生成者) 来进行这种不突变的计算( 生成者)优惠透明Racket使用一些中间语言对初始方案进行一系列转换,其中之一是进行这种改写,使产量经营者与较简单的经营者以某种语言进行转换。

这是如何重写产量的演示, 它使用 R6RS 的结构, 但语义与 Python 的相同 。 这是相同的计算模式, 只需要修改语法, 才能使用 Python 的 产量重写 。

Welcome to Racket v6.5.0.3.

-> (define gen
     (lambda (l)
       (define yield
         (lambda ()
           (if (null? l)
               'END
               (let ((v (car l)))
                 (set! l (cdr l))
                 v))))
       (lambda(m)
         (case m
           ('yield (yield))
           ('init  (lambda (data)
                     (set! l data)
                     'OK))))))
-> (define stream (gen '(1 2 3)))
-> (stream 'yield)
1
-> (stream 'yield)
2
-> (stream 'yield)
3
-> (stream 'yield)
'END
-> ((stream 'init) '(a b))
'OK
-> (stream 'yield)
'a
-> (stream 'yield)
'b
-> (stream 'yield)
'END
-> (stream 'yield)
'END
->

(我下面的回答只从使用Python发电机的角度,而不是从使用Python发电机的角度,而不是从使用Python发电机的角度来回答发电机机制基本实施,这涉及一些玩弄堆叠和堆积操纵的把戏。 ))

何时yield使用代替return在 python 函数中,该函数被转换为特殊的东西,称为generator function该函数返回generator类型。缩略yield关键字是通知 Python 编译者专门处理此函数的标志。正常函数一旦从中返回某些值, 正常函数就会终止。 但是, 在编译器的帮助下, 生成器函数将会终止 。能够被想象到即,执行环境将恢复,执行将持续到最后一年。直到你明确要求返回,这会引起StopIteration选项(这也是迭代协议的一部分),或达到函数的结尾。我发现很多关于generator但这个1个调自自functional programming perspective是最可消化的。

(现在我想谈一下为什么generatoriterator我希望这能帮助你掌握基本动机和基本动机这一概念以其他语言出现,如C#。 )

据我所知,当我们想要处理一堆数据时, 我们通常先把数据存放在某处,然后一个一个地处理。但是这个是。幼天如果数据量很大, 事先将数据全部储存起来是昂贵的 。而不是储存data为什么不直接储存某种metadata间接,即:the logic how the data is computed.

有两种方法可以包扎这类元数据。

  1. OO 方法,我们包封元数据as a class这就是所谓的iterator执行滚动协议(即__next__(), 和__iter__()这也是人们所普遍看到的方法。电动电机设计图案.
  2. 功能方法,我们包封元数据as a function这就是所谓的generator function但是在兜帽帽下, 返回的人generator object仍为IS-A因为它还执行传动协议 。

无论哪种方式, 都会创建一个迭代器, 即某个可以提供您想要的数据的对象。 OO 处理方式可能有点复杂。 总之, 由您决定使用哪一种 。

yield允许您更聪明地写字for- 通过将循环部分计入一个便于再利用的单独方法。

假设你需要环绕电子表格的所有非空白行,对每行都做一些事情。

for i, row in df.iterrows(): #from the panda package for reading excel 
  if row = blank: # pseudo code, check if row is non-blank...
    continue
  if past_last_row: # pseudo code, check for end of input data
    break
  #### above is boring stuff, below is what we actually want to do with the data ###
  f(row)

如果你需要打电话g(row)在一个类似的循环中,你可能会发现自己重复for语句加有效行的检查,这是枯燥、复杂和易出错的。我们不想重复(DRY 原则) 。

您想要将检查每个记录的代码与实际处理行的代码区分开来, 比如f(row)g(row) .

您可以做一个函数, 将 f() 作为输入参数, 但使用要简单得多yield在一个方法中做所有关于检查有效行以准备拨打 f () 的无聊内容:

def valid_rows():
  for i, row in df.iterrows(): # iterate over each row of spreadsheet
    if row == blank: # pseudo code, check if row is non-blank...
      continue
    if past_last_row: # pseudo code, check for end of input data
      break
    yield i, row

请注意,方法的每次调用将返回下一行,但如果所有行都读取,且for结束, 方法将return通常。下一次调用将开始新的for循环。

现在您可以在数据上写入迭代, 而不必重复对有效行进行无趣的检查( 现在根据自己的方法来计算) , 例如 :

for i, row in valid_rows():
  f(row)

for i, row in valid_rows():
  g(row)

nr_valid_rows = len(list(valid_rows()))

仅此而已。 请注意, 我还没有使用诸如 迭代器、 生成器、 协议、 共同常规等术语 。 我认为这个简单的例子 适用于我们日常的许多编码 。