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

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被调用时会发生什么?是列表返回?一个单一的元素?是否再次被调用?随后的电话何时会停止?

答案:
要理解什么yield,你必须了解什么是 generators。并且在 generators 来之前迭代。

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,file…

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

Generators

生成器是迭代器,一种迭代器,只能迭代一次。发生器不会将所有的值存储在内存中,它们会立即生成值

>>> 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

yield是一个使用的关键字return,除了函数将返回一个生成器。

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

 

这是一个无用的例子,但是当你知道你的函数将会返回一大堆你只需要读取一次的值的时候,它是很方便的。

要掌握yield,你必须明白当你调用函数时,你写在函数体中的代码不会运行。该函数只返回生成器对象,这有点棘手:-)

然后,您的代码将在每次for使用生成器时运行。

现在困难的部分:

第一次for调用由你的函数创建的generator对象时,它会从你的函数开始直到碰到它yield,然后返回第一个循环的值。然后,每个其他的调用将运行你已经写在函数中的循环一次,并返回下一个值,直到没有值返回。

一旦函数运行,发生器被认为是空的,但不再被yield触发。这可能是因为循环已经结束,或者因为你不再满足"if/else"了。

 

你的代码解释

Generator:

# 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 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 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 candidates 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期望iterables,所以它将与字符串,列表,元组和生成器一起工作!这就是所谓的鸭子打字,也是Python如此酷的原因之一。但是,这是另一个故事,另一个问题…

你可以在这里停下来,或者稍微阅读一下看看生成器的高级用法:

控制 generator 耗尽

>>> 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 # 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)]

了解迭代的内在机制

迭代是一个暗示迭代(实现__iter__()方法)和迭代(实现__next__()方法)的过程。Iterables是可以从中获取迭代器的任何对象。迭代器是可以迭代迭代的对象。

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

添加评论

友情链接:蝴蝶教程