Python 中产出关键字的用法是什么? 它能做什么?

例如,我试图理解这个代码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_camedates 被调用时会怎样? 列表是否返回? 单一个元素吗? 是否再次调用? 以后的电话何时停止?


1. 本代码由Jochen Schulz(jrschulz)编写,他为公制空间制作了一个伟大的Python图书馆,与完整的源:模块mspace链接。


当前回答

佩顿有什么差错?

Python 中的 Yield 关键字类似于用于返回 Python 中的值或对象的返回语句。 但是, 存在微小的差别。 收益语句返回一个生成符, 而不是简单地返回一个值, 而返回一个函数的生成符。

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

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

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

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

资料来源:https://www.simplilearn.com/tutorics/python-tutoric/yield-in-python。

其他回答

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.

每当你发现自己从头开始编出一个清单时, 每一块都取而代之。

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


收成是一种含糖的方式 说

构建一系列材料

相同行为 :

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

不同的行为 :

产量是单行道,只能绕过一次。当一个函数有收益时,我们称它为发电机功能。循环者就是它的回报。这些术语是明亮的。我们失去了一个容器的方便,但获得一系列按需要计算并任意延长的能量。

是懒惰的, 它会推卸计算。 函数中含有收益的函数在调用时不会实际执行。 它返回一个循环器对象, 记得它留下的位置。 每次您在调用循环器时( 这发生在换环) 执行步数向下一个产数前进。 返回会提高停止输出并结束序列( 这是换圈的自然结束 ) 。

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

>>> 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(square_yield(4))
[0, 1, 4, 9]

英明地选择“产生”一词,因为这两个含义都适用:

产量——生产或供应(如农业)

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

放弃或放弃(与政治权力一样)

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

收益率与返回率相似。区别是:

函数输出使函数可循环( 在以下示例中, 质数( n= 1) 函数成为可循环的 )。 它基本上意味着下次调用函数时, 它会从它离开的地方( 以产出表达式的线为后方) 继续 。

def isprime(n):
    if n == 1:
        return False
    for x in range(2, n):
        if n % x == 0:
            return False
    else:
        return True

def primes(n = 1):
   while(True):
       if isprime(n): yield n
       n += 1 

for n in primes():
    if n > 100: break
    print(n)

在上述例子中, 如果是inprime( n) 是真实的, 它会返回质号。 在下一个迭代中, 它会从下一行继续

n += 1  

产量的另一种用途和含义(自Python3.3以来):

yield from <expr>

PEP380 -- -- 属于子发电机的语法:

提议对发电机使用语法将部分操作权下放给另一个发电机。 这样可以将含有“ ield” 的代码部分输入到另一个发电机中。 此外, 允许次发电机返回一个值, 并将价值提供给授权发电机。 新的语法也为当一台发电机再生价值时实现优化开辟了某些机会 。

此外,这将引入(自Python3.5以来):

async def new_coroutine(data):
   ...
   await blocking_action()

以避免与正常发电机混为一谈(两者均使用每天的产量)。

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

我猜你已经得知回程声明了

作为类比,回归和收益是双胞胎。 回归意味着“ 回归和停止 ” , 而“ 回归”则意味着“回归,但继续 ” 。

尝试获得一份有回报的 num_ 列表 。

def num_list(n):
    for i in range(n):
        return i

运行它:

In [5]: num_list(3)
Out[5]: 0

你看,你只得到一个数字,而不是一个他们的名单。返回永远不允许你快乐地获胜,只要执行一次就退出。

产生结果

将返回替换为产出 :

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]

现在,你赢得了所有的数字。

与一次运行和停止的返回相比, 一次运行和一次运行, 一次运行和一次运行。 您可以将返回解释为一个返回, 一次返回作为全部返回。 这叫“ 易动 ” 。

再多走一步,我们就可以重新写出回报的收益声明

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]

这是关于产量的核心。

列表返回输出与目标产出的区别是:

您总是可以从列表对象中获取 [0, 1, 2] , 但只能从“ 对象输出输出” 中提取一次 。 因此, 它有一个新的名称生成对象, 如 Out[ 11] 所示 : <generator 对象 num_ list at 0x10327c990> 。

最后,作为格罗克语的比喻:

双胞胎名单和发电机是双胞胎

理解产出的快捷键

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

在函数开始处插入行结果 = []。 以结果替换每个输出。 附录( 扩展) 。 在函数底部插入一行返回结果 。 耶 - 不再生成语句! 读取并解析代码。 将函数与原始定义比较 。

这个骗局也许能让你了解函数背后的逻辑, 但实际的收益率与列表法中发生的情况大不相同。 在许多情况下, 收益率法会提高记忆效率和速度。 在其他情况下, 这个骗局会让你陷入一个无限的循环, 即使最初的功能运作良好。 阅读以学习更多...

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

首先,当您写作时的循环程序协议

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

Python 执行以下两个步骤:

为我的列表获取一个代号 : 调用 exer( mylist) - > 这返回一个具有下一个( ) 方法( 或 __ next__ () () 在 Python 3 中) 的对象 [这是大多数人忘记告诉你 使用传动器环绕项目的步骤 : 继续调用从第 1 步返回的代名器上的下一个( ) 方法 。 下一个( ) 的返回值被指定给 x , 循环体被执行 。 如果从下一个( ) 中提出例外 停止 , 这意味着在循环器中没有更多的值, 循环被退出 。

真相是 Python 执行上述两个步骤, 每当它想绕过对象的内容时, 都会执行上述两个步骤 - 所以它可以是环绕, 但它也可以像其它列表一样是代码 。 extendend( mylist) ( 其中其他列表是 Python 列表 ) 。

这里的我的列表是可替换的, 因为它执行的是循环协议 。 在用户定义的类别中, 您可以使用 ` iter__ () 方法使分类的循环性实例可以被使用。 此方法应该返回一个循环器。 循环器是一个带有下一个( ) 方法的对象。 在同一类中可以同时执行 _ iter__ () 和 下一个( ) , 并有 _ iter__ () 返回自我 。 这将对简单案例有效, 但当您想要两个循环器同时绕过同一个对象时则不行 。

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

内置列表、 词典、 图普尔、 集和文件。 执行 ` iter__ () 的用户定义的分类 。 发电机 。

注意“ 循环” 并不知道它所处理的物体是什么类型 - 它只是遵循了循环程序, 并且乐意在下一个( ) 调用时按项目逐项获得项目 。 内建列表逐项返回项目, 字典逐项返回关键词, 文件逐行返回行等 。 而发电机则返回... 也就是产出来源所在 :

def f123():
    yield 1
    yield 2
    yield 3

for item in f123():
    print item

而不是输出语句, 如果您在 f123 () 中有三个返回语句, 只有第一个将被执行, 而函数会退出 。 但是 f123 () 并不是普通函数 。 当调用 f123 () 时, 它不会返回产值语句中的任何值 。 它返回一个生成对象 。 另外, 该函数并不真正退出 - 它会进入一个中止状态 。 当循环尝试在生成对象上循环时, 函数会从先前返回的产值之后的下一行的中止状态恢复到下一行的状态, 执行下一行代码, 在此情况下, 产生语句, 并返回为下一个项目 。 这一直发生到函数退出, 此时, 生成器将启动暂停, 以及循环退出 。

因此,生成器对象有点像一个适配器 — — 在一端,它展示了迭代程序, 暴露了 `iter___ () 和下一个 () 方法来保持循环的快乐。 但是,在另一端, 它运行着功能, 足以将下一个值调出, 并把它放回中止模式 。

为什么使用发电机?

通常情况下, 您可以写入不使用发电机的代码, 但执行相同的逻辑。 一个选项是使用我之前提到的临时列表“ trick ” 。 这不会在所有情况下都有效, 比如, 如果您有无限环, 或者当您有非常长的列表时它可能无效地使用内存 。 另一种方法是执行一个新的可循环的类别“ 某些东西 ” , 将国家保留在成员中, 并在下一个( ) ( 或 Python 3 ) 方法中执行下一个逻辑步骤 。 根据逻辑, 下一个( ) 方法中的代码可能最终会查找非常复杂和易被错误的代码 。 在这里, 生成器可以提供一个简单明了的解决方案 。