何为使用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 语法语法使用yieldKywit 关键字可以使函数返回 a发电机发电机.

  • 发电机是一种振动器,这就是在Python发生环绕的主要方式。

  • 发电机基本上是一种可消耗的功能。return返回一个数值,然后结束一个函数,即yield关键字关键字返回一个值并暂停一个函数。

  • 何时next(g)调用一个发电机,函数在剩余部分恢复执行。

  • 只有当函数遇到明示或默示return它实际上结束了。

书写和理解发电机技术

理解和思考发电机的一个简单的方法就是 写一个常规功能print()代替yield:

def f(n):
    for x in range(n):
        print(x)
        print(x * 10)

注意它的产出:

>>> f(3)
0
0
1
10
2
2

当该函数被理解时,替换yield用于print获得产生相同数值的生成器:

def f(n):
    for x in range(n):
        yield x
        yield x * 10

给 :

>>> list(f(3))
[0, 0, 1, 10, 2, 20]

迭代程序协议

答案“什么产量能做什么”可以是简短和简单的, 但是它是更大的世界的一部分, 所谓的“标准协议”。

在迭代协议的发送方,有两种相关对象。易可动的你可以环绕过去的东西。振动器是跟踪环状状态的对象。

在循环协议的消费者方面,我们呼叫erier()在可循环的物体上获得一个迭代器。然后我们拨打下一个( )用于从迭代器中检索值的迭代器上的迭代器。当不再有数据时, a停止试提出例外:

>>> s = [10, 20, 30]    # The list is the "iterable"
>>> it = iter(s)        # This is the "iterator"
>>> next(it)            # Gets values out of an iterator
10
>>> next(it)
20
>>> next(it)
30
>>> next(it)
Traceback (most recent call last):
 ...
StopIteration

为了让这一切变得更容易, 对于卢布人来说,叫它, 下一个代表我们:

>>> for x in s:
...     print(x)
...   
10
20
30

一个人可以写一本关于这一切的书, 但这些都是关键点。 当我教授 Python 课程时, 我发现这是一个最起码的足够解释 来建立理解 并马上开始使用它。 特别是, 写一个函数的把戏print,测试它,然后转换成yield似乎与所有级别的Python程序员合作良好。

其他回答

许多人使用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()给发电机。

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

失败给了你一台发电机

def get_odd_numbers(i):
    return range(1, i, 2)
def yield_odd_numbers(i):
    for x in range(1, i, 2):
       yield x
foo = get_odd_numbers(10)
bar = yield_odd_numbers(10)
foo
[1, 3, 5, 7, 9]
bar
<generator object yield_odd_numbers at 0x1029c6f50>
bar.next()
1
bar.next()
3
bar.next()
5

如你所见,第一种情况foo将整个列表同时保留在记忆中。 对于包含 5 个元素的列表来说, 这不是什么大问题, 但如果您想要 5 百万 的列表, 那又会怎样 ? 这不仅仅是一个巨大的记忆食用器, 在函数被调用时, 它还要花费很多时间来构建 。

在第二个案件中,bar发电机是可循环的 也就是说你可以用在for循环等, 但每个值只能存取一次 。 所有值也并非同时存储在记忆中; 生成器对象“ Remember ” 。 上次您称之为循环时, 生成器对象“ remember ” 正在循环中, 这样, 如果您正在使用一个可( 说) 的转号, 计为 500 亿, 那么您不必同时计为 500 亿, 然后存储500 亿 个数字来进行计算 。

再者,这是一个相当巧妙的例子,如果你真想数到500亿,你可能会使用滑板。 () :

这是发电机中最简单的使用实例。 正如您所说, 它可以用来写高效的变换, 使用产量将东西推到调用堆叠上, 而不是使用某种堆叠变量。 发电机也可以用于专门的树道, 以及各种其它方式 。

要理解发电机的产量功能,人们必须理解发电机是什么。 此外,在理解发电机之前,你必须理解易可动的。可操作性:对于创建列表,您自然需要能够逐项阅读每个元素。逐项阅读其项目的过程称为迭代:

>>> mylist = [1, 2, 3]
>>> for i in mylist:
...    print(i)
1
2
3 

My list 是可替换的。 当您使用列表理解值时, 您会创建一个列表, 因此该列表是可替换的 :

>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
...    print(i)
0
1
4 

所有可用于... 的数据结构都是可循环的; 列表、 字符串、 文件...

这些惯用方法很方便,因为您可以随意阅读,但您可以将所有值存储在记忆中,当您有许多值时,这些值并不总是可取的。 生成器: 生成器 A 也是一种迭代器, 一种特殊的迭代器, 只能迭代一次。 生成器不会将所有值存储在记忆中, 而是在苍蝇上生成值 :

发电机:发电机、发电机、发电机发电,但不储存能源;)

>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
...    print(i)
0
1
4 

只要使用 () 而不是 [] , 列表理解就会变成发电机理解。 但是, 由于发电机只能使用一次, 您无法在我的生成器中执行 i 第二次 : 生成器计算 0, 然后丢弃它, 然后计算 1, 最后一次计算 4 。 典型的黑色盲人打破玉米 。

产出关键字的使用方式与返回相同,但函数返回生成器。

>>> def createGenerator():
...    mylist = range(3)
...    for i in mylist:
...        yield i*i
...
>>> mygenerator = createGenerator() 
>>> print(mygenerator) 
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
...     print(i)
0
1
4 

这个例子本身是毫无用处的,但是当您需要函数返回大量数值,而只需要读一次,使用产量就方便了。

要掌握收益率,需要清楚的是,当函数被调用时,函数正文中写入的代码将不会运行。函数只返回生成对象。启动者可能会对此感到困惑。

第二,明白代码会从每次使用发电机时留下的代码中继续使用。

现在最困难的部分是:

第一次调用您函数所创建的生成器对象时, 它会运行函数中的代码, 从开始一直运行到产生, 然后返回循环的第一个值。 然后, 以后的每次调用都会运行您在函数中写入的循环的下一个迭代, 并返回下一个值。 这将一直持续到生成器被视为空, 当函数运行时没有被击中时该生成。 这可能是因为循环已经结束, 或者因为您不再满足于“ if/ else ” 。

个人理解 我希望帮助你!

我不太熟悉Python, 但我相信它和Python一样C# 的迭代器区块如果你熟悉这些。

关键的想法是,编译者/解释者/ 不论做什么诡计, 就打电话者而言, 他们可以继续拨打下一个 () , 它会继续返回数值 :仿佛发电机方法被暂停。现在显然你无法真正“暂停”一种方法,因此编译器可以建立一个状态机器,以便你记住你目前的位置和本地变量等的外观。这比自己写一个转动器容易得多。

要理解的快捷键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()方法最终可能会看起来非常复杂,容易出现虫子。 这里的发电机提供了清洁和容易的解决方案。