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

当前回答

想象一下, 你创造了一个非凡的机器, 能够每天生成成千上万个灯泡。 机器用一个独特的序列号的盒子生成这些灯泡。 您没有足够的空间同时存储所有这些灯泡, 所以您想要调整它来生成点燃灯泡 。

Python 生成器与这个概念没有什么不同。 想象一下, 您有一个函数叫做 Python 。barcode_generator以生成框中独有的序列号。 显然,您可以通过函数返回大量这样的条形码,但受硬件(RAM)的限制。 更明智和空间效率更高的选项是按需生成这些序列号。

机器代码 :

def barcode_generator():
    serial_number = 10000  # Initial barcode
    while True:
        yield serial_number
        serial_number += 1


barcode = barcode_generator()
while True:
    number_of_lightbulbs_to_generate = int(input("How many lightbulbs to generate? "))
    barcodes = [next(barcode) for _ in range(number_of_lightbulbs_to_generate)]
    print(barcodes)

    # function_to_create_the_next_batch_of_lightbulbs(barcodes)

    produce_more = input("Produce more? [Y/n]: ")
    if produce_more == "n":
        break

注注:next(barcode)位数。

如你所可以看到,我们有一个自成一体的“功能” 每次生成下一个独特的序列号。此函数返回发电机发电机正如你可以看到的,我们不是每次需要新序列号时都调用这个功能,而是在使用新序列号。next()给发电机提供下一个序列号。

低拉隔热器

更确切地说,这个发电机是懒惰的滚动器迭代器是一个能帮助我们穿越物体序列的物体。 它被称为懒惰因为它在需要之前不会在内存中装入序列的全部项目。next在上一个示例中,直 直 直从迭代器获取下一个项目。内含循环方式正在使用 :

for barcode in barcode_generator():
    print(barcode)

这将无穷尽地打印条形码, 但你不会失去内存 。

换句话说,发电机看起来像a 函数但行为举止如迭代器。

现实世界应用?

最后, 真实世界应用程序 。 当您在大序列中工作时, 它们通常有用 。 想象一下读取巨大从含有数十亿记录的磁盘文件中取出文件。 在您能够处理其内容之前, 在内存中读取整个文件, 可能会不可行( 也就是说, 您会用完内存 ) 。

其他回答

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

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

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

要理解的快捷键yield

当您看到一个函数yield语句,应用这个简单易懂的把戏来理解会发生什么:

  1. 插入一行result = []3⁄4 ̄ ̧漯B
  2. 替换各yield exprresult.append(expr).
  3. 插入一行return result函数的底部。
  4. - 耶 - 不再yield语句! 读取并找出代码 。
  5. 将函数与原始定义比较。

这个把戏也许能让你了解 函数背后的逻辑, 但实际发生什么了?yield与以列表为基础的方法发生的情况大不相同。 在许多情况下, 收益率方法会提高记忆效率和速度。 在其他情况下, 这个把戏会使你陷入无穷无尽的循环中, 即使最初的函数效果很好。 阅读更多来学习...

不要弄乱你的循环器 循环器和发电机

首先,动态自动交换协议- 当你写作时

for x in mylist:
    ...loop body...

Python 执行以下两个步骤:

  1. 获得一个循环器用于mylist:

    调调iter(mylist)->此返回一个带有next()方法(或)__next__()Python 3 中。

    [这是大多数人忘记告诉你的一步]

  2. 使用迭代器绕过项目 :

    继续叫next()从第1步返回的迭代器上的迭代器 方法上的迭代器 。next()指定用于x并执行环环体。如果有例外StopIteration从内部筹集next(),这意味着循环器中没有更多的值,循环就退出了。

真相是 Python 随时随地执行上述两步环绕环绕对象的内容 - 所以它可能是循环的, 但它也可以是代码otherlist.extend(mylist)(此处(此处)otherlist是 Python 列表)。

mylist易 易 易 性因为它执行了循环协议。在用户定义的类中,您可以执行__iter__()使类的示例可易易易操作的方法。 此方法应该返回振动器对象。next()两种方法都可实施。__iter__()next()在同一类同级同级同级同级同级同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同班同同同班同班同班同班同班同同班同班同班同班同班同班同同同班同班同班同班同班同同班同同同同同班同班同班同班同同班同班同班同班同同同同同同同班同班同班同同同同同同同班同同同同同同班同__iter__()返回返回self。这将对简单案例有效,但当您想要两个迭代器同时绕过同一个对象时,则不会有效。

这就是传动程序,许多物体执行这个程序:

  1. 内置列表、词典、图普尔、设置和文件。
  2. 执行的用户定义的分类__iter__().
  3. 发电机。

注 afor循环不知道它处理的是什么样的物体 - 它只是遵循循环程序, 并且很乐意按项目逐项获得它调用的项目next(). 内置清单逐项归还其物项,词典则逐项归还键键一个一个一个一个,文件返回线条一个一个一个一个一个,等等。 和发电机返回。 。 。yield输入 :

def f123():
    yield 1
    yield 2
    yield 3

for item in f123():
    print item

取代yield如果您有三种语句return以 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国 国f123()只有第一个被执行, 而功能会退出。 但是,f123()没有普通函数为普通函数的普通函数为普通函数。f123()被召唤的,被召唤的,被召唤的,被召唤的,返回输出语句中的任何值。它返回生成对象。它返回一个生成对象。此外,函数并不真正退出 -它进入中止状态。当for循环试图环绕到发电机对象上, 函数从中止状态恢复 。yield执行下一行代码,在此情况下,ayield语句,然后返回该语句,作为下一个项目返回该语句。这种情况发生到函数退出时,然后生成器产生StopIteration,并循环出口。

因此,发电机对象有点像一个适配器—— 一方面,它展示了迭代程序,通过曝光__iter__()next()保存for循环快乐。 但是,在另一端, 它运行的函数足够从中获取下一个值, 并将其重新置于暂停模式 。

为什么使用发电机?

通常情况下, 您可以写入不使用发电机的代码, 但使用相同的逻辑逻辑。 一个选项是使用我之前提到的临时列表“ trick ” 。 这不会在所有情况下都有效, 例如, 如果您有无限循环, 或者当您有很长的列表时它可能无效地使用内存 。 另一种方法是执行一个新的可循环的类“ 某些东西 ” , 将国家保留在成员中, 并在成员中执行下一个逻辑步骤 。next()(或)__next__()Python 3 方法中的代码 。 取决于逻辑, 内部的代码 。next()方法最终可能会看起来非常复杂,容易出现虫子。 这里的发电机提供了清洁和容易的解决方案。

虽然很多答案 表明你为什么会使用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计算 fibonacci 序列的基础方法,解释如下:

def fib(limit=50):
    a, b = 0, 1
    for i in range(limit):
       yield b
       a, b = b, a+b

当你把这个输入你的REPL,然后尝试把它称为, 你会得到一个神秘的结果:

>>> fib()
<generator object fib at 0x7fa38394e3b8>

这是因为:yield发送到 Python 的信号, 您想要创建发电机发电机,即,一个根据需求产生价值的物体。

那么,您如何生成这些值? 可以通过使用内置函数直接实现next,或间接地,通过将其喂养到消耗价值的建筑上。

使用内置next()函数,直接引用.next/__next__迫使发电机产生一个价值:

>>> g = fib()
>>> next(g)
1
>>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> next(g)
5

间接提供fib至 afor环环, alist初始初始化器, atuple初始化器, 或其他任何期望生成/ 产生值的对象, 您会“ 组装” 生成器, 直到它无法生成更多值( 并返回 ) :

results = []
for i in fib(30):       # consumes fib
    results.append(i) 
# can also be accomplished with
results = list(fib(30)) # consumes fib

同样,tuple初始化器 :

>>> tuple(fib(5))       # consumes fib
(1, 1, 2, 3, 5)

生成器与功能不同, 因为它很懒。 它通过保持本地状态, 并允许您在需要的时候恢复运行来达到这个目的 。

当你们第一次祈祷的时候,fib称其为:

f = fib()

Python 编译函数,遇到yieldkeyword and simply return a generate objects back at you. 似乎没有什么帮助。

当您要求它生成第一个值时,它直接或间接地执行它发现的所有语句,直到它遇到一个yield,然后,它产生回 价值,你提供yield并暂停。 举例来证明这一点, 让我们使用一些print电话(取代电话)print "text"如果Python 2 上写着:

def yielder(value):
    """ This is an infinite generator. Only use next on it """ 
    while 1:
        print("I'm going to generate the value for you")
        print("Then I'll pause for a while")
        yield value
        print("Let's go through it again.")

现在,输入REPL:

>>> gen = yielder("Hello, yield!")

您现在有一个生成对象, 正在等待命令来生成值。 使用next并查看打印的内容 :

>>> next(gen) # runs until it finds a yield
I'm going to generate the value for you
Then I'll pause for a while
'Hello, yield!'

未引用的结果是打印的内容。引用的结果是返回的内容yield调来next现在再次:

>>> next(gen) # continues from yield and runs again
Let's go through it again.
I'm going to generate the value for you
Then I'll pause for a while
'Hello, yield!'

发电机记得它被停停在yield value从那里打印下一条消息并搜索yield暂停该语句时(由于while(循环))

什么是yieldPython的关键字是用吗?

答复大纲/摘要

  • 函数函数函数函数yield当呼唤,返回返回 a发电机发电机.
  • 发电机是迭代器,因为它们实施动态自动交换协议这样你就可以绕过他们了
  • 发电机也可以已发送信息使它在概念上成为共礼管.
  • 在Python 3,你可以代表代表从一个发电机到另一个发电机,双向yield from.
  • (附录对几个答案,包括前一个答案,并讨论使用return在发电机中。 )

发电机:

yield只是在功能定义中的法律内涵,而且列入《公约》yield在函数定义中,函数定义使其返回生成器。

发电机的构想来自其他语文(见脚注1),其实施方式各有不同。冻结当发电机被调用(方法将在下文讨论)时,恢复执行,然后冻结在下一个产地。

yield简易的给养,执行循环协议由以下两种方法界定:__iter____next__。这两种方法都使对象成为可键入检查对象的迭代器。Iterator基础摘要类collections模块。

def func():
    yield 'I am'
    yield 'a generator!'

让我们进行一些反省:

>>> type(func)                 # A function with yield is still a function
<type 'function'>
>>> gen = func()
>>> type(gen)                  # but it returns a generator
<type 'generator'>
>>> hasattr(gen, '__iter__')   # that's an iterable
True
>>> hasattr(gen, '__next__')   # and with .__next__
True                           # implements the iterator protocol.

生成器类型是一个子迭代器类型 :

from types import GeneratorType
from collections.abc import Iterator

>>> issubclass(GeneratorType, Iterator)
True

如有必要,我们可以这样打字检查:

>>> isinstance(gen, GeneratorType)
True
>>> isinstance(gen, Iterator)
True

A. 单Iterator 即已用尽,您不能重新使用或重置它:

>>> list(gen)
['I am', 'a generator!']
>>> list(gen)
[]

如果你想再次使用其功能,你必须再做一次(见脚注2):

>>> list(func())
['I am', 'a generator!']

可以按方案生成数据,例如:

def func(an_iterable):
    for item in an_iterable:
        yield item

上述简单生成器也相当于以下生成器 -- -- 从Python 3.3来看,您可以使用yield from:

def func(an_iterable):
    yield from an_iterable

不过,yield from还允许向次级发电机授权,将在下一节 " 与子大麻合作的代表团 " 中加以解释。

计票:

yield窗体中的表达式,该表达式允许将数据发送到生成器(见脚注3)

举一个例子,请注意received变量,该变量将指向发送到生成方的数据:

def bank_account(deposited, interest_rate):
    while True:
        calculated_interest = interest_rate * deposited 
        received = yield calculated_interest
        if received:
            deposited += received


>>> my_account = bank_account(1000, .05)

首先,我们必须排队 发电机与内建功能,nextnext__next__方法, 取决于您使用的 Python 版本 :

>>> first_year_interest = next(my_account)
>>> first_year_interest
50.0

现在我们可以把数据传送到发电机里了发送中发送None与调用相同next.) :

>>> next_year_interest = my_account.send(first_year_interest + 1000)
>>> next_year_interest
102.5

合作代表团yield from

现在,回顾yield fromPython 3 提供。 这让我们可以将 共同路线 委托给子coustine :


def money_manager(expected_rate):
    # must receive deposited value from .send():
    under_management = yield                   # yield None to start.
    while True:
        try:
            additional_investment = yield expected_rate * under_management 
            if additional_investment:
                under_management += additional_investment
        except GeneratorExit:
            '''TODO: write function to send unclaimed funds to state'''
            raise
        finally:
            '''TODO: write function to mail tax info to client'''
        

def investment_account(deposited, manager):
    '''very simple model of an investment account that delegates to a manager'''
    # must queue up manager:
    next(manager)      # <- same as manager.send(None)
    # This is where we send the initial deposit to the manager:
    manager.send(deposited)
    try:
        yield from manager
    except GeneratorExit:
        return manager.close()  # delegate?

现在我们可以将功能委托给一个子生成器 并且它可以被一个发电机使用 就像上面那样:

my_manager = money_manager(.06)
my_account = investment_account(1000, my_manager)
first_year_return = next(my_account) # -> 60.0

现在模拟在账户中再增加1000, 加上账户的回报( 60.0 ) :

next_year_return = my_account.send(first_year_return + 1000)
next_year_return # 123.6

您可以阅读更多关于yield fromPEP380号

其他方法:关闭和投掷

缩略close方法生成GeneratorExit功能执行被冻结时, 这也会被调用 。__del__以便您可以设置任何清理代码, 用于处理GeneratorExit:

my_account.close()

您也可以丢弃一个例外,该例外可在生成器中处理,或向用户传播:

import sys
try:
    raise ValueError
except:
    my_manager.throw(*sys.exc_info())

提高:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
  File "<stdin>", line 6, in money_manager
  File "<stdin>", line 2, in <module>
ValueError

结论 结论 结论 结论 结论

我认为,我已处理了下列问题的所有方面:

什么是yieldPython的关键字是用吗?

事实证明yield我肯定我可以为此再增加更详尽的例子。如果你们想要更多或有建设性的批评,请在下面评论,让我知道。


附录:

顶级/接受的答复的优先程度**

  • 它被混淆了 是什么制造了易 易 易 性,仅以列表作为示例。见以上我的引用,但摘要:a易 易 易 性具有 a 的__iter__返回方法返回振动器。 A. A.振动器额外提供a.__next__方法,该方法隐含地称为:for循环直到它升起StopIteration,一旦它升起StopIteration它将继续这样做。
  • 然后,它用发电机表达式来描述发电机是什么。 因为发电机表达式只是创建一个振动器,它只是混淆了事情, 而我们仍然还没有达到yield部分。
  • 控制发电机耗竭他呼唤.next方法(仅在Python 2 中有效),当他应该使用内设函数时,next调用next(obj)这是一种适当的间接分解层,因为他的代码在Python 3号行不通。
  • 这跟什么无关yield完全没有。
  • 不讨论采用哪些方法yield与新功能一起提供yield fromPython 3 号的Python 3。

上方/接受的回答是一个非常不完整的回答。

答复的精度答复建议yield中显示或理解生成方的表达或理解。

语法目前允许列表理解中的任何表达式 。

expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
                     ('=' (yield_expr|testlist_star_expr))*)
...
yield_expr: 'yield' [yield_arg]
yield_arg: 'from' test | testlist

由于产量是一种表达方式,有些人认为在理解或生成方表达方式中使用产量是令人感兴趣的,尽管没有提出特别好的使用方式。

CPython 核心开发者是讨论其津贴的折旧问题从邮件列表中找到一个相关的邮件 :

2017年1月30日19:05时,

2017年1月29日17:39Craig Rodrigues写道:

我不管用哪一种方法都行,把事情和在Python 3的一样丢在一边是不好的,IMHO。

我的投票是 语法错误,因为你没有得到 你期望的语法。

我同意这对我们来说是个明智的地方 因为任何依靠当前行为的守则 都太聪明了 无法维持下去

就到达那里而言,我们很可能想要:

  • 3.7年的语法警告或折旧警告
  • 2.7.x.x.中的Py3k警告
  • 3.8 中的语法rror

干杯,尼克。

- Nick Coghlan ncoghlan at gmail.com Brisbane, 澳大利亚布里斯班

此外,还有未决问题(10544)这一点似乎指着着《公约》和《公约》从未是一个好主意(PyPy, PyPython 的 Python 执行书写在 Python 上, 已经在提高语法警告 。 )

直至CPython的开发商告诉我们:别放yield中显示或理解生成方的表达或理解。

缩略return生成器中的语句

Python 3:

在发电机功能中,return声明中表示发电机已经完成,并将导致StopIteration返回的值(如果有的话)用作构建的论据。StopIteration成为StopIteration.value属性。

历史记录,Python 2: "在发电机功能中,return不允许包含语句expression_list在这种背景下,光return表示发电机完成并导致StopIteration将升起。expression_list基本上是用逗号分隔的任意数表达式 - 基本上是在 Python 2 中, 您可以用return,但你不能返回一个值。

脚注脚注

  1. 在向Python引入发电机概念的提案中,提到了CLU、Sather和“图标”等语言。 通常的想法是,功能可以维持内部状态,并产生用户需要的中间数据点。 这保证了这一功能能够维持内部状态,并产生中间数据点。业绩优于其他方法,包括Python线在某些系统上甚至都找不到

  2. 这意味着,例如,range对象不是物体Iteratori 尽管它们是可循环的, 因为它们可以被再利用。就像列表一样,它们__iter__方法返回迭代对象。

  3. yield最初作为声明被引入,意思是它只能出现在代码块的线条开头。yield创建收益表达式 。https://docs.python.org/2/reference/simple_stmts.html#grammar-token-yield_stmt这一变化是:拟议拟议拟议数允许用户像收到数据一样将数据发送到生成器中。 要发送数据, 就必须能够将数据指定给某些东西, 因此, 声明是行不通的 。