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

当前回答

就像每个答案都暗示的那样yield用于创建序列生成器。 它用于动态生成某些序列。 例如, 在网络上逐行读取文件行时, 您可以使用yield函数如下:

def getNextLines():
   while con.isOpen():
       yield con.read()

您可在您的代码中使用以下代码:

for line in getNextLines():
    doSomeThing(line)

执行控制控制

执行控制将从下拉林( GetNextLines) 转到for当输出被执行时循环。 因此, 每次引用 NextLines () 时, 执行从上次暂停的点开始 。

因此,简言之,一个函数具有以下代码

def simpleYield():
    yield "first time"
    yield "second time"
    yield "third time"
    yield "Now some useful value {}".format(12)

for i in simpleYield():
    print i

将打印

"first time"
"second time"
"third time"
"Now some useful value 12"

其他回答

yield就像有人要你做5个蛋糕。如果你做了至少一个蛋糕, 你可以在做其他蛋糕的时候给他们吃。

In [4]: def make_cake(numbers):
   ...:     for i in range(numbers):
   ...:         yield 'Cake {}'.format(i)
   ...:

In [5]: factory = make_cake(5)

factory称为“发电机”的发电机,它使你们做蛋糕。如果你们打电话,make_function,而不是运行此函数。这是因为当yield关键字在函数中,它成为生成器。

In [7]: next(factory)
Out[7]: 'Cake 0'

In [8]: next(factory)
Out[8]: 'Cake 1'

In [9]: next(factory)
Out[9]: 'Cake 2'

In [10]: next(factory)
Out[10]: 'Cake 3'

In [11]: next(factory)
Out[11]: 'Cake 4'

他们消耗了所有的蛋糕, 但他们又要求一个。

In [12]: next(factory)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-12-0f5c45da9774> in <module>
----> 1 next(factory)

StopIteration:

有人命令他们不要多问一些问题。一旦你消耗了一台发电机,你就用完它了。你应当打电话,make_cake如果你想要更多蛋糕,就再来一次。这就像给蛋糕再订一份蛋糕一样。

In [13]: factory = make_cake(3)

In [14]: for cake in factory:
    ...:     print(cake)
    ...:
Cake 0
Cake 1
Cake 2

您也可以使用上面的生成器来循环。

举个例子:假设你每次问密码时都想要随机密码。

In [22]: import random

In [23]: import string

In [24]: def random_password_generator():
    ...:     while True:
    ...:         yield ''.join([random.choice(string.ascii_letters) for _ in range(8)])
    ...:

In [25]: rpg = random_password_generator()

In [26]: for i in range(3):
    ...:     print(next(rpg))
    ...:
FXpUBhhH
DdUDHoHn
dvtebEqG

In [27]: next(rpg)
Out[27]: 'mJbYRMNo'

rpg是一个生成器, 它可以生成无限数量的随机密码。 所以我们也可以说, 当我们不知道序列的长度时, 生成器是有用的, 而不是列表中含有数量有限的元素 。

理解什么yield确实,你必须明白什么是发电机发电机。在您能够理解发电机之前,您必须理解易可动的.

易变性

创建列表时,您可以逐项阅读其项目。逐项阅读其项目被称为迭代:

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

mylist易 易 易 性。当您使用对列表的理解时,会创建列表,因此,可以循环:

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

能够使用的一切 " 。for... in..."是可循环的;lists, strings文档...

这些可替换的功能是实用的,因为您可以随心所欲地阅读,但您将所有值都存储在记忆中,当您拥有很多值时,这并不总是你想要的。

发电机发电机

发电机是迭代器,是一种可循环的您只能循环一次。发电机不会存储所有值的内存,它们会在飞上生成值:

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

除了你用过的一样()代替[]但是,你,你无法不能表现 表现表现for i in mygenerator第二次,因为发电机只能使用一次:它们计算0,然后忘记它,计算1,最后计算4,一个一个。

产量d

yield是一个关键字,它被像return,但该函数将返回一个发电机。

>>> def create_generator():
...    mylist = range(3)
...    for i in mylist:
...        yield i*i
...
>>> mygenerator = create_generator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
<generator object create_generator at 0xb7555c34>
>>> for i in mygenerator:
...     print(i)
0
1
4

这是一个毫无用处的例子, 但当你知道你的功能会返回 一大堆的值时, 它就方便了, 你只需要读一次。

师傅yield你必须明白当您调用函数时,函数体中的代码不会运行。函数只返回生成对象, 这有点棘手 。

然后,你的代码会继续 从它每次离开的代码开始for使用发电机。

现在,硬的部分:

第一次for调用从您函数创建的生成器对象,它将运行您函数中的代码,从开始一直运行到点击yield,然后它返回循环的第一个值。然后,每次随后的呼叫将运行您在函数中写入的循环的再次迭代,然后返回下一个值。这将一直持续到发电机被视为空,当函数运行时没有打中yield。这可能是因为循环已经结束,或者因为你不再满足"if/else".


您的代码解释

发电机:

# Here you create the method of the node object that will return the generator
def _get_child_candidates(self, distance, min_dist, max_dist):

    # Here is the code that will be called each time you use the generator object:

    # If there is still a child of the node object on its left
    # AND if the distance is ok, return the next child
    if self._leftchild and distance - max_dist < self._median:
        yield self._leftchild

    # If there is still a child of the node object on its right
    # AND if the distance is ok, return the next child
    if self._rightchild and distance + max_dist >= self._median:
        yield self._rightchild

    # If the function arrives here, the generator will be considered empty
    # there are no more than two values: the left and the right children

调用者 :

# Create an empty list and a list with the current object reference
result, candidates = list(), [self]

# Loop on candidates (they contain only one element at the beginning)
while candidates:

    # Get the last candidate and remove it from the list
    node = candidates.pop()

    # Get the distance between obj and the candidate
    distance = node._get_dist(obj)

    # If the distance is ok, then you can fill in the result
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)

    # Add the children of the candidate to the candidate's list
    # so the loop will keep running until it has looked
    # at all the children of the children of the children, etc. of the candidate
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))

return result

本代码包含几个智能部分 :

  • 循环在列表中反复出现, 但列表会扩展, 而循环正在迭代中 。 这是一个简洁的方法 来查看所有这些嵌套的数据, 即使它有点危险, 因为您可以以无限循环结束 。 在这种情况下,candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))耗尽发电机的所有值,但while保持创建新生成的生成对象, 从而产生与前一个生成对象不同的值, 因为它不应用在同一节点上 。

  • 缩略extend()方法是一种列表对象方法,该方法预计可循环并增加其值到列表中。

通常,我们向它传递一份清单:

>>> a = [1, 2]
>>> b = [3, 4]
>>> a.extend(b)
>>> print(a)
[1, 2, 3, 4]

但在你的代码中,它有一个发电机, 这是很好的,因为:

  1. 您不需要两次阅读数值 。
  2. 你可能有很多孩子 你不想把他们都保存在记忆中

之所以有效,是因为 Python 并不在意一种方法的论据是否是一个列表。 Python 期望它能用字符串、列表、图普勒和生成器来操作。 这叫做鸭字打字, 也是Python之所以如此酷的原因之一。 但是这是另一个故事, 另一个问题...

您可以在这里停下来,或者读一下,看一个生成器的先进使用:

控制发电机耗竭

>>> class Bank(): # Let's create a bank, building ATMs
...    crisis = False
...    def create_atm(self):
...        while not self.crisis:
...            yield "$100"
>>> hsbc = Bank() # When everything's ok the ATM gives you as much as you want
>>> corner_street_atm = hsbc.create_atm()
>>> print(corner_street_atm.next())
$100
>>> print(corner_street_atm.next())
$100
>>> print([corner_street_atm.next() for cash in range(5)])
['$100', '$100', '$100', '$100', '$100']
>>> hsbc.crisis = True # Crisis is coming, no more money!
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> wall_street_atm = hsbc.create_atm() # It's even true for new ATMs
>>> print(wall_street_atm.next())
<type 'exceptions.StopIteration'>
>>> hsbc.crisis = False # The trouble is, even post-crisis the ATM remains empty
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> brand_new_atm = hsbc.create_atm() # Build a new one to get back in business
>>> for cash in brand_new_atm:
...    print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...

注:Python 3, 用于 Python 3, 使用print(corner_street_atm.__next__())print(next(corner_street_atm))

它可以对控制获取资源等各种事情有用。

义大便,你最好的朋友

Itertools 模块包含操作可替换文件的特殊功能 。 是否想要重复生成器? 连锁二生成器? 组值与单线串连接的嵌入列表中?Map / Zip不创建其它列表吗 ?

然后,就刚刚import itertools.

举个例子,让我们看看四匹马赛的到货订单

>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)
<itertools.permutations object at 0xb754f1dc>
>>> print(list(itertools.permutations(horses)))
[(1, 2, 3, 4),
 (1, 2, 4, 3),
 (1, 3, 2, 4),
 (1, 3, 4, 2),
 (1, 4, 2, 3),
 (1, 4, 3, 2),
 (2, 1, 3, 4),
 (2, 1, 4, 3),
 (2, 3, 1, 4),
 (2, 3, 4, 1),
 (2, 4, 1, 3),
 (2, 4, 3, 1),
 (3, 1, 2, 4),
 (3, 1, 4, 2),
 (3, 2, 1, 4),
 (3, 2, 4, 1),
 (3, 4, 1, 2),
 (3, 4, 2, 1),
 (4, 1, 2, 3),
 (4, 1, 3, 2),
 (4, 2, 1, 3),
 (4, 2, 3, 1),
 (4, 3, 1, 2),
 (4, 3, 2, 1)]

了解迭代的内部机制

迭迭代是一个过程,意味着可迭代(实施__iter__()和迭代器(执行__next__()循环是您可以从中获取迭代器的任何对象。迭代器是允许您在迭代器上迭代的对象。

这篇文章中更多关于如何如何for环环工作.

下面是浅白语言的例子。我将提供高层次人类概念与低层次Python概念之间的对应关系。

我想用数字序列操作, 但我不想用这个序列的创建来烦恼我自己, 我只想专注于我想做的操作。 因此, 我做以下工作:

  • 我打电话给你,告诉你,我想要一个数字序列 以特定的方式计算, 我让你知道算法是什么。
    此步骤对应于def内插入发电机函数,即包含yield.
  • 稍后,我告诉你, "好了,准备好告诉我数字的顺序"。
    此步骤对应于调用发电机函数, 以返回发电机对象 。注意不要告诉我任何数字 你只要拿起你的纸和铅笔
  • 我问你,"告诉我下一个号码",然后你告诉我第一个号码, 在那之后,你等我问你下一个号码。你的工作是记住你在哪里,你已经说过什么号码,下一个号码是什么。 我不在乎细节。
    此步骤对应于调用next(generator)在发电机的物体上。
    (在Python 2,.next是产生器物体的一种方法;在Python 3中,它被命名为.__next__,但正确的称呼方式是使用内置next()函数类似len().__len__)
  • ...重复前一步,直到...
  • 最终,你可能会走到尽头。你不会告诉我一个数字;你只会喊叫,“抓住你的马!我受够了!不再有数字了!”
    此步骤对应于生成器对象结束工作, 并提升StopIteration例外。
    生成器函数不需要提出例外。 当函数结束或发布时自动生成 。return.

这就是发电机(包含yield;它开始执行第一个next()时暂停yield,当要求next()它从最后一点继续值 。 它的设计完全符合 Python 的迭代协议, 它描述了如何按顺序请求值 。

循环程序最著名的用户是for在 Python 中命令。 所以, 当您做 :

for item in sequence:

这不重要,如果sequence是列表、字符串、字典或生成器对象对象如上文所述;结果相同:您逐个阅读顺序中的项目。

请注意def函数内含有yield关键字不是创建生成器的唯一方法; 它只是创建生成器的最简单的方法 。

将 " 更准确的信息 " 改为 " 更准确的信息 "迭代器类型、、 和收益单报表发电机发电机在 Python 文档中。

简单解答

函数至少包含一个时yield语句,函数自动成为发电机功能。当您调用发电机功能时, python 在发电机功能中执行代码,直到yield发生声明。yield当您再次调用发电机功能时, python 继续从冻结位置执行发电机功能中的代码,直到yield发电机函数执行代码直到发电机功能用完时没有yield语句。

基准基准基准基准基准基准基准

创建列表并返回它 :

def my_range(n):
    my_list = []
    i = 0
    while i < n:
        my_list.append(i)
        i += 1
    return my_list

@profile
def function():
    my_sum = 0
    my_values = my_range(1000000)
    for my_value in my_values:
        my_sum += my_value

function()

结果有:

Total time: 1.07901 s
Timer unit: 1e-06 s

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     9                                           @profile
    10                                           def function():
    11         1          1.1      1.1      0.0      my_sum = 0
    12         1     494875.0 494875.0     45.9      my_values = my_range(1000000)
    13   1000001     262842.1      0.3     24.4      for my_value in my_values:
    14   1000000     321289.8      0.3     29.8          my_sum += my_value



Line #    Mem usage    Increment  Occurences   Line Contents
============================================================
     9   40.168 MiB   40.168 MiB           1   @profile
    10                                         def function():
    11   40.168 MiB    0.000 MiB           1       my_sum = 0
    12   78.914 MiB   38.746 MiB           1       my_values = my_range(1000000)
    13   78.941 MiB    0.012 MiB     1000001       for my_value in my_values:
    14   78.941 MiB    0.016 MiB     1000000           my_sum += my_value

在飞行上生成值 :

def my_range(n):
    i = 0
    while i < n:
        yield i
        i += 1

@profile
def function():
    my_sum = 0
    
    for my_value in my_range(1000000):
        my_sum += my_value

function()

结果有:

Total time: 1.24841 s
Timer unit: 1e-06 s

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     7                                           @profile
     8                                           def function():
     9         1          1.1      1.1      0.0      my_sum = 0
    10
    11   1000001     895617.3      0.9     71.7      for my_value in my_range(1000000):
    12   1000000     352793.7      0.4     28.3          my_sum += my_value



Line #    Mem usage    Increment  Occurences   Line Contents
============================================================
     7   40.168 MiB   40.168 MiB           1   @profile
     8                                         def function():
     9   40.168 MiB    0.000 MiB           1       my_sum = 0
    10
    11   40.203 MiB    0.016 MiB     1000001       for my_value in my_range(1000000):
    12   40.203 MiB    0.020 MiB     1000000           my_sum += my_value

摘要摘要摘要

生成器函数需要稍多一点时间来执行, 而不是返回列表但少用内存的函数 。

这是关于什么的心理形象yield确实如此。

我想把一条线视为有堆叠(即使它不是用这种方式执行的)。

当调用一个普通函数时, 它会将其本地变量放入堆栈, 进行一些计算, 然后清除堆栈和返回。 其本地变量的值再也不会被看到 。

yield函数,当其代码开始运行时(即函数被调用后,返回一个生成对象,该生成对象next()然后引用方法),它同样将其本地变量放在堆叠上,并计算一段时间。但是当它击中yield语句,在清理其部分堆叠并返回之前,它先对本地变量进行速记,然后将其存储在生成器对象中。它还写下它目前在其代码中的位置(即特定yield声明))

所以这是一种冷冻功能 发电机挂在了上面

何时next()函数随后被调用, 它从堆叠上取回函数的物品, 并重新激活它。 函数继续从剩余部分进行计算, 忽略了它刚刚在冷藏中度过了永恒时间的事实 。

比较以下实例:

def normalFunction():
    return
    if False:
        pass

def yielderFunction():
    return
    if False:
        yield 12

当我们调用第二个函数时,它的行为与第一个功能非常不同。yield声明可能无法取得, 但如果它存在任何地方, 它会改变我们所处理的事物的性质。

>>> yielderFunction()
<generator object yielderFunction at 0x07742D28>

电 电 电yielderFunction()(也许用它来命名这种东西是个好主意)yielder可读性前缀。 )

>>> gen = yielderFunction()
>>> dir(gen)
['__class__',
 ...
 '__iter__',    #Returns gen itself, to make it work uniformly with containers
 ...            #when given to a for loop. (Containers return an iterator instead.)
 'close',
 'gi_code',
 'gi_frame',
 'gi_running',
 'next',        #The method that runs the function's body.
 'send',
 'throw']

缩略gi_codegi_frame字段中存储冻结状态的字段。dir(..),我们可以确认 我们的心理模式 上面是可信的。