“yield“关键字有什么作用?

文章来源:"Yield"关键字有什么作用? - 代码领悟code05.com代码领悟(code05.com)问答库主要帮助程序员在开发过程中遇到的程序编程BUG问题以及DEBUG解决方法。https://www.code05.com/question/detail/231767.html

要了解yield的作用,您必须了解生成器是什么。 在了解生成器之前,您必须了解iterables

迭代器

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

复制代码>>> 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..."的所有内容都是可迭代的;listsstrings,文件。..

这些iterables很方便,因为您可以随心所欲地读取它们,但是您将所有值存储在内存中,当您有很多值时,这并不总是您想要的。

发电机

生成器是迭代器,一种可迭代的你只能迭代一次。 生成器不会将所有值存储在内存中,它们会动态生成值:

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

除了你使用()而不是[]之外,它是一样的。 但是,您不能第二次执行for i in mygenerator,因为生成器只能使用一次:它们计算0,然后忘记它并计算1,并结束计算4,一个接一个。

产量

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时。 这可能是因为循环已经结束,或者因为你不再满足an"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 is 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 distance is ok, then you can fill the result
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)

    # Add the children of the candidate in the candidate's list
    # so the loop will keep running until it will have 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需要迭代器,因此它将与字符串、列表、元组和生成器一起工作! 这被称为duck typing,也是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
...

注意:对于Python3,使用print(corner_street_atm.__next__())print(next(corner_street_atm))

它对于控制对资源的访问等各种事情都很有用。

Itertools,你最好的朋友

itertools模块包含用于操作iterables的特殊函数。 曾经想复制一个发电机吗? 链两个发电机? 在一个单行嵌套列表中分组值? 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)]

理解迭代的内在机制

迭代是一个隐含iterables(实现__iter__()方法)和iterators(实现__next__()方法)的过程。 Iterables是您可以从中获取迭代器的任何对象。 迭代器是允许您迭代可迭代对象的对象。

在这篇文章中有更多关于for循环如何工作的内容。

文章来源:"Yield"关键字有什么作用? - 代码领悟code05.com代码领悟(code05.com)问答库主要帮助程序员在开发过程中遇到的程序编程BUG问题以及DEBUG解决方法。https://www.code05.com/question/detail/231767.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值