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发电机的角度,而不是从发电机机制的基本实施角度,后者涉及一些堆叠和堆积操纵的伎俩。 )

当在 python 函数中使用 quot 而不是返回时, 该函数将被转换为特殊的名称 。 此函数将返回生成器类型的对象 。 产量关键字是提醒 python 编译器专门处理此函数的旗帜 。 正常函数一旦从中返回某些值就会终止 。 但是, 在编译器的帮助下, 生成器的函数可以被视为可恢复 。 也就是说, 执行环境将会恢复, 执行会从上次运行时继续 。 直到您明确调回, 这会引起一个停止引力例外( 也是循环协议的一部分) , 或者到达函数的终点 。 我发现许多关于生成器的引用, 但从功能编程角度来说, 这是一种最可消化的引用 。

(现在我想谈谈产生者背后的理由, 以及基于我自己的理解的循环器。 我希望这能帮助你掌握循环器和生成者的基本动机。 这一概念以其他语言出现, 如 C# 。 )

据我所知,当我们想要处理一大批数据时,我们通常先在某处储存数据,然后逐个处理。但这种天真的方法有问题。如果数据量很大,那么事先将数据全部储存起来费用很高。因此,与其直接储存数据本身,不如间接储存某种元数据,即数据计算逻辑。

有两种方法可以包扎这类元数据。

OO 方法, 我们把元数据包成一个类。 这是执行循环协议( 即 __ next_ () 和 __ ter_ () 方法) 的所谓迭代器。 这也是常见的迭代器设计模式 。 功能方法, 我们将元数据包成函数 。 这是所谓的生成功能 。 但是在引擎盖下, 返回的生成对象仍然是 IS - A 迭代器, 因为它也执行循环程序 。

无论哪种方式, 都会创建一个迭代器, 即某个可以提供您想要的数据的对象。 OO 处理方式可能有点复杂。 总之, 由您决定使用哪一种 。

其他回答

这样想吧:

迭代器只是具有下一个( ) 方法的对象的奇特探测术语。 因此, 产生式的函数最终会变成这样 :

原文:

def some_function():
    for i in xrange(4):
        yield i

for i in some_function():
    print i

Python 翻译用上述代码所做的基本上就是:

class it:
    def __init__(self):
        # Start at -1 so that we get 0 when we add 1 below.
        self.count = -1

    # The __iter__ method will be called once by the 'for' loop.
    # The rest of the magic happens on the object returned by this method.
    # In this case it is the object itself.
    def __iter__(self):
        return self

    # The next method will be called repeatedly by the 'for' loop
    # until it raises StopIteration.
    def next(self):
        self.count += 1
        if self.count < 4:
            return self.count
        else:
            # A StopIteration exception is raised
            # to signal that the iterator is done.
            # This is caught implicitly by the 'for' loop.
            raise StopIteration

def some_func():
    return it()

for i in some_func():
    print i

为了更深入地了解幕后发生的事情,

iterator = some_func()
try:
    while 1:
        print iterator.next()
except StopIteration:
    pass

这更有意义还是更让人困惑?

我要指出,为了说明起见,这过于简单化。 )

用于创建生成器 。 将生成器想象成一个迭代器 highc , 给您每个迭代值 。 当您在循环中使用 收益率 时, 就会得到一个生成器对象, 您可以用该对象从循环中以迭接方式从循环中获取项目 。

要了解什么是产量,你必须了解什么是发电机。在你能够理解发电机之前,你必须了解易燃的发电机。

易变性

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

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

你可以使用的一切"... 在..."是一个可循环的; 列表,字符串,文件...

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

发电机发电机

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

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

除了使用()而不是使用()之外,它是一样的。但是,由于发电机只能使用一次,所以不能在我的生成器中为我第二次执行,因为发电机只能使用一次:它们计算0,然后忘记它,然后计算1,然后结束计算4,一个一个一个地计算。

产量d

函数将返回一个生成器。

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

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

要掌握输出能力, 您必须明白当您调用函数时, 您在函数体中写入的代码没有运行。 函数只返回生成对象, 这有点棘手 。

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

现在,硬的部分:

第一次调用您函数所创建的生成器对象时, 它会运行您函数的代码, 从开始到它产生, 然后返回循环的第一个值。 然后, 以后每次调用都会运行您在函数中写入的循环的再次迭代, 然后返回下一个值。 这将一直持续到生成器被认为是空的, 当函数运行时不会打出收益。 这可能是因为循环结束, 或者因为您不再满足“ 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

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

在列表中循环迭代, 但列表会随着循环迭代而扩展。 这是一个简单的方式来查看所有这些嵌套的数据, 即使它是一个有点危险的, 因为您可以以无限环结束。 在此情况下, 候选人 。 extendend( rode._ get_ child_ camedates( root, min_ dist, max_ distist)) 将耗尽所有生成器的值, 但同时继续创建新生成的生成对象, 这些对象将产生与先前的相异的值, 因为它不会被应用到同一个节点上 。 扩展 () 方法是一种列表对象方法, 期待一个可重复的列表对象方法, 并将其添加到列表中 。

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

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

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

你不需要两次阅读这些值。 你可能有很多孩子, 你不想把他们都保存在记忆中。

之所以有效,是因为 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, 使用打印( corner_street_atm._next___ ()) 或打印( ext( corner_ street_ atm) )

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

义大便,你最好的朋友

Itertool 模块包含操作可替换文件的特殊功能 。 是否想要复制一个生成器? 连锁二生成器? 组值在单行的嵌套列表中? 地图/ Zip 不创建另一个列表 ?

然后就进口它的工具。

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

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

了解迭代的内部机制

迭代是一个过程, 意味着可迭代( 实施 _ etre_ () 方法) 和迭代( 实施 ext_ () 方法) 。 迭代是您可以从中获取迭代器的任何对象。 迭代器是允许您循环到可迭代的物体 。

本文中有更多关于环环如何运作的论述。

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

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

In [5]: factory = make_cake(5)

这里称为发电机, 它会做蛋糕。 如果您叫作 Make_ 函数, 您可以得到一个发电机, 而不是运行此函数。 这是因为当输出关键字出现在一个函数中时, 它会变成一个生成器 。

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:

并且他们被告知不要问更多的问题。 所以一旦你消耗了发电机, 你就会用它做。 如果你想要更多的蛋糕,你需要再打电话做蛋糕。 这就像再订一份蛋糕。

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 是一个生成器, 它可以生成无限数量的随机密码。 所以我们也可以说, 当我们不知道序列的长度时, 生成器是有用的, 不同于列表, 列表中有一定数量的元素 。

- 功能 - 返回。

发电机 -- -- 产量(含有一个或多个产量和零或更多回报率)。

names = ['Sam', 'Sarah', 'Thomas', 'James']


# Using function
def greet(name) :
    return f'Hi, my name is {name}.'
    
for each_name in names:
    print(greet(each_name))

# Output:   
>>>Hi, my name is Sam.
>>>Hi, my name is Sarah.
>>>Hi, my name is Thomas.
>>>Hi, my name is James.


# using generator
def greetings(names) :
    for each_name in names:
        yield f'Hi, my name is {each_name}.'
 
for greet_name in greetings(names):
    print (greet_name)

# Output:    
>>>Hi, my name is Sam.
>>>Hi, my name is Sarah.
>>>Hi, my name is Thomas.
>>>Hi, my name is James.

发电机看起来像一个函数,但行为举止却像一个迭代器。

发件人继续从它所在的位置执行 。 恢复后, 函数在最后产值运行后立即继续执行 。 这允许它的代码在一段时间内生成一系列的值, 代之以它们一次性计算全部值, 然后把它们像列表一样送回去 。

def function():
    yield 1 # return this first
    yield 2 # start continue from here (yield don't execute above code once executed)
    yield 3 # give this at last (yield don't execute above code once executed)

for processed_data in function(): 
    print(processed_data)
    
#Output:

>>>1
>>>2
>>>3

注:放弃不应在尝试中.最终建造。