何为使用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 函数但行为举止如迭代器。

现实世界应用?

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

其他回答

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

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 函数但行为举止如迭代器。

现实世界应用?

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

从方案拟订的角度来看,迭代器是按下列方式执行的: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
->

什么是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这一变化是:拟议拟议拟议数允许用户像收到数据一样将数据发送到生成器中。 要发送数据, 就必须能够将数据指定给某些东西, 因此, 声明是行不通的 。

python 的输出与返回语句类似,但有些差异除外。如果要从函数返回多个值,返回语句将把所有值都作为列表返回,并将其存储在调用符块的内存中。但如果我们不想使用额外的内存,会怎样?相反,我们需要在需要时从函数中获取该值。这是产出的来源。考虑以下函数:

def fun():
   yield 1
   yield 2
   yield 3

打电话的人是:

def caller():
   print ('First value printing')
   print (fun())
   print ('Second value printing')
   print (fun())
   print ('Third value printing')
   print (fun())

上述代码段(调用函数),如果调用,产出:-

First value printing
1
Second value printing
2
Third value printing
3

从上文可以看出, 产出返回其调用器的值, 但当函数再次调用时, 它不会从第一个语句开始, 而是从产出后右侧的语句开始。 在上述示例中, “ 第一值打印” 打印, 函数被调用。 1 被回传并打印。 然后, 打印“ 第二值打印” , 并再次调用有趣 () 。 它不打印 1 (第一个语句) , 而是返回 2 , 也就是说, 仅从产出 1 之后的语句 。 同样的程序会进一步重复 。

这里所有的答案都是伟大的,但其中只有一个答案(最受投票支持的答案)是真实的。您的代码如何工作其他涉及发电机发电机一般而言,以及它们如何运作。

所以,我不重复发电机是什么或产量是什么;我认为这些都包含在现有的答案中。然而,在花了几个小时试图理解一个与你的代码相似的代码之后,我将打破它是如何运作的。

您的代码绕过二进制树结构。 让我们以这棵树为例:

    5
   / \
  3   6
 / \   \
1   4   8

另一个简单的二进制搜索树的十字路口:

class Node(object):
..
def __iter__(self):
    if self.has_left_child():
        for child in self.left:
            yield child

    yield self.val

    if self.has_right_child():
        for child in self.right:
            yield child

执行代码在Tree对象,该对象执行__iter__以此:

def __iter__(self):

    class EmptyIter():
        def next(self):
            raise StopIteration

    if self.root:
        return self.root.__iter__()
    return EmptyIter()

缩略while candidates语句可以替换为for element in tree; Python 翻译为

it = iter(TreeObj)  # returns iter(self.root) which calls self.root.__iter__()
for element in it: 
    .. process element .. 

因为Node.__iter__代码里面执行时按迭代执行。 所以执行时会是这样的 :

  1. 根元素是第一个; 检查它是否留下了子子和for切换它们( 我们称它为1, 因为它的第一个迭代对象) 。
  2. 它有一个孩子,所以for执行。for child in self.left创建 a 创建新建新迭代器调自self.left,它是一个节点对象本身(it2)
  3. 和2的逻辑相同 和新的逻辑iterator创建(it3)
  4. 现在我们到达树的左边it3他们没有儿女,所以它是继续的,yield self.value
  5. 下通电话next(it3)它产生StopIteration并且存在,因为它没有子女的权利(直到功能结束,没有任何成果)
  6. it1it2仍在活动----他们没有用尽,也没有召唤。next(it2)将产生价值,而不是提高StopIteration
  7. 现在我们又回到it2和调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调- 调-next(it2)直至它停止时:紧接着yield child语句。由于它不再留下孩子,所以它继续并产生它self.val.

这里的下场是 每一次迭代创建子编辑器以绕过树, 并保持当前迭代器的状态。 一旦它到达尾端, 它就会绕过堆叠, 并按正确的顺序返回值( 最低的收益率先返回 ) 。

您的代码示例在不同的技术中 做了类似的事情: 它包含一个单元素列表每个子子对每个子子子, 然后在下一个迭代中, 它弹出它, 运行当前对象的函数代码( 因此)self).

我希望这对这个传奇话题有一点帮助,我花了好几个小时来画这个过程来理解它。