何为使用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 中的 Yield 关键字类似于用于返回 Python 中的值或对象的返回语句。 但是, 存在微小的差别。 收益语句返回一个生成符, 而不是简单地返回一个值, 而返回一个函数的生成符。

在程序内,当您调用一个函数,该函数有一个输出语句时,一旦遇到一个输出,函数的执行即停止,然后将生成器的一个对象返回到函数调用器。用更简单的文字,产出关键字将把一个与该关键字一起指定的表达式转换为生成器对象,然后返回到调用器。因此,如果您想要获得在生成器对象内存储的值,则需要将该关键字复制到该对象上。

它不会破坏本地变量的状态。 当调用函数时, 执行将从最后一个输出表达式开始。 请注意, 包含输出关键字的函数被称为生成函数 。

当您使用含有返回值的函数时,每次调用函数时,该函数从一组新的变量开始。反之,如果使用一个生成函数而不是正常函数,则执行将从它左最后的位置开始。

如果您想要从函数中返回多个值, 您可以使用输出关键字来使用生成函数。 输出表达式返回多个值。 它们返回一个值, 然后等待, 保存本地状态, 然后再恢复 。

资料来源:https://www.simplilearn.com/tutorials/python-tutorial/yield-in-python

其他回答

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

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没有提供同声糖:

作为Python发电机:

from itertools import islice

def fib_gen():
    a, b = 1, 1
    while True:
        yield a
        a, b = b, a + b

assert [1, 1, 2, 3, 5] == list(islice(fib_gen(), 5))

使用地法关闭代替发电机

def ftake(fnext, last):
    return [fnext() for _ in xrange(last)]

def fib_gen2():
    #funky scope due to python2.x workaround
    #for python 3.x use nonlocal
    def _():
        _.a, _.b = _.b, _.a + _.b
        return _.a
    _.a, _.b = 0, 1
    return _

assert [1,1,2,3,5] == ftake(fib_gen2(), 5)

使用关闭物体代替发电机(因为封闭和对象等等同)

class fib_gen3:
    def __init__(self):
        self.a, self.b = 1, 1

    def __call__(self):
        r = self.a
        self.a, self.b = self.b, self.a + self.b
        return r

assert [1,1,2,3,5] == ftake(fib_gen3(), 5)

要理解的快捷键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计算 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(循环))

放弃是一个对象

A A Areturn在函数中返回单一值。

如果您愿意,如果需要函数返回一大批值,使用yield.

更重要的是yield是 a 是障碍屏障.

就像CUDA语言中的屏障, 它不会转移控制 直到它完成。

也就是说,它会运行您函数的代码 从开始直到启动yield。然后,它将返回循环的第一个值。

然后,其他每通电话都会运行您在函数中写下的循环, 返回下一个值, 直到没有任何值可以返回 。