何为使用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要创建生成器, 有更多的用途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()

其他回答

就像每个答案都暗示的那样yield用于创建序列生成器。 它用于动态生成某些序列。 例如, 在网络上逐行读取文件行时, 您可以使用yield函数如下:

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

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

for line in getNextLines():
    doSomeThing(line)

执行控制控制

执行控制将从下拉林( GetNextLines) 转到for当输出被执行时循环。 因此, 每次引用 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"

缩略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:

  • 可以通过停止函数从函数返回一个值多次。
  • 可使用from和它一样yield from.
  • 用于返回大数据时,将其分为小部分数据,以防止大量使用内存。

例如,test()可在以下返回'One', 'Two'['Three', 'Four']以一一一一一一一一一一一一一停止test()so so, so, so, so, so, so, so, so, so,test()停止返回共3倍test()总共3次:

def test():
    yield 'One'                  # Stop, return 'One' and resume 
    yield 'Two'                  # Stop, return 'Two' and resume
    yield from ['Three', 'Four'] # Stop and return ['Three', 'Four'] 

下面这三套代码可以调用test()打印和打印'One', 'Two', 'Three''Four':

for x in test():
    print(x)
x = test()
print(next(x))
print(next(x))
print(next(x))
print(next(x))
x = test()
print(x.__next__())
print(x.__next__())
print(x.__next__())
print(x.__next__())

其结果是:

$ python yield_test.py
One
Two
Three
Four

此外,在使用returnyield,没有办法从return:

def test():
    yield 'One' 
    yield 'Two'
    yield from ['Three', 'Four']
    return 'Five' # 'Five' cannot be got

x = test()
print(next(x))
print(next(x))
print(next(x))
print(next(x))
print(next(x)) # Here

因此,在试图获取'Five':

$ python yield_test.py 
One
Two
Three
Four
Traceback (most recent call last):
  File "C:\Users\kai\yield_test.py", line 12, in <module>
    print(next(x))
          ^^^^^^^
StopIteration: Five

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

我猜你已经学会了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双胞胎

许多人使用return而不是yield,但在某些情况下yield能够更有效和更方便地开展工作。

以下是一个例子:yield绝对是最好的:

返回返回(在职能)

import random

def return_dates():
    dates = [] # With 'return' you need to create a list then return it
    for i in range(5):
        date = random.choice(["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th"])
        dates.append(date)
    return dates

收益率(在职能)

def yield_dates():
    for i in range(5):
        date = random.choice(["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th"])
        yield date # 'yield' makes a generator automatically which works
                   # in a similar way. This is much more efficient.

呼叫功能

dates_list = return_dates()
print(dates_list)
for i in dates_list:
    print(i)

dates_generator = yield_dates()
print(dates_generator)
for i in dates_generator:
    print(i)

两种功能都做相同的事情,但yield使用三行而不是五行, 并有一个更少的变量需要担心 。

这是代码的结果:

Output

正如你可以看到两个函数都做相同的事情。唯一的区别是return_dates()给出列表并yield_dates()给发电机。

真实生活中的范例就是 逐行读取文件行 或者你只是想制造一个发电机