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 生成器与这个概念没有多大区别。 想象一下, 您有一个叫做条形码_ 生成器的函数, 可以为框生成独特的序列号 。 显然, 您可以在硬件( RAM) 的限制下, 由函数返回大量这样的条形码 。 一个更明智和空间效率更高的选项是按需生成这些序列号 。

机器代码 :

def barcode_generator():
    serial_number = 10000  # Initial barcode
    while True:
        yield serial_number
        serial_number += 1


barcode = barcode_generator()
while True:
    number_of_lightbulbs_to_generate = int(input("How many lightbulbs to generate? "))
    barcodes = [next(barcode) for _ in range(number_of_lightbulbs_to_generate)]
    print(barcodes)

    # function_to_create_the_next_batch_of_lightbulbs(barcodes)

    produce_more = input("Produce more? [Y/n]: ")
    if produce_more == "n":
        break

注意下个( 条码) 位 。

如你所见, 我们有一个自足的“ 功能” , 每次生成下一个独特的序列号。 此函数返回一个生成器 。 正如您所看到的, 我们并不是每次我们需要一个新的序列号时都会调用该功能, 而是使用下一个( ) , 给生成器来获取下一个序列号 。

低拉隔热器

更精确地说, 这个生成器是一个懒惰的循环器 。 循环器是一个帮助我们绕过一个天体序列的物体。 它被称为懒惰, 因为它在需要之前不会在内存中装入序列中的所有项目。 上一个示例中的下一个是从循环器获取下一个项目的清晰方式 。 隐含方式用于循环 :

for barcode in barcode_generator():
    print(barcode)

这将无穷尽地打印条形码, 但你不会失去内存 。

换句话说,一个发电机看起来像一个函数,但行为却像一个迭代器。

现实世界应用?

最后, 真实世界应用程序 。 当您在大序列中工作时, 它们通常是有用的 。 想象一下从有数十亿记录的磁盘上读取一个巨大的文件。 在您能够处理其内容之前, 在记忆中读取整个文件, 很可能是行不通的( 也就是说, 您将失去记忆 ) 。

其他回答

总之,产出语句将您的函数转换成一个工厂,该工厂生产一个特殊对象,称为发电机,围绕您原始函数的正文包绕。当生成器被迭代时,它将执行您的函数,直到达到下一个输出时,然后中止执行,然后对传递到的数值进行评估。它重复了每次迭代的这一过程,直到执行路径退出函数。例如,

def simple_generator():
    yield 'one'
    yield 'two'
    yield 'three'

for i in simple_generator():
    print i

简单产出

one
two
three

电源来自使用循环计算序列的生成器, 生成器执行循环每次停止到“ ield ” 的下一个计算结果, 这样它就可以计算飞行上的列表, 好处是存储到特别大的计算中的内存

说你想创建一个自己的范围函数, 产生一个可循环的数字范围, 你可以这样做,

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

并像这样使用它;

for i in myRangeNaive(10):
    print i

但这效率低,因为

您创建了一个只使用一次的数组( 此废物内存) 。 此代码实际上会两次循环到该数组上 ! : () : ()

幸好吉多和他的团队 慷慨地开发了发电机 这样我们就可以这么做了

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

for i in myRangeSmart(10):
    print i

在每次迭代中, 调用下一个发电机的函数执行该函数, 直至它到达“ ield” 语句停止和“ ields” 值, 或到达函数的终点。 在第一次调用的情况下, 下一个( ) 执行到产出语句, 并产生“ n ” , 下次调用它将执行递增语句, 跳回“ 此时” , 评估它, 如果是, 它会停止并再次产生“ n ” , 它会一直持续到状态返回错误, 发电机跳到函数结束的时候 。

关键要点

Python 的语法库使用输出关键字的存在来设定返回生成器的函数。 生成器是一种迭代器, 这是一种在 Python 中循环的主要方式。 生成器本质上是一个可回收的函数。 与返回返回值和结束函数不同的是, 生成关键字返回一个值和中止函数。 当下一个( g) 被调用到生成器时, 函数会恢复它所剩的功能。 只有当函数遇到一个明示或暗示返回时, 它才会实际结束 。

书写和理解发电机技术

一个理解和思考发电机的简单方法就是用印刷品()而不是产量来撰写一个常规函数:

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

注意它的产出:

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

3⁄4 ̄ ̧漯B

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

给 :

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

迭代程序协议

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

在迭代协议的发送方,有两种相关的对象。可循环的物体是您可以绕过的东西。迭代器是跟踪环状状态的物体。

在迭代协议的消费方方面,我们调用可迭代对象的迭代者获得一个迭代者。然后我们调用下一个迭代者从迭代器中获取值。当没有更多数据时,会提出一个停止使用例外 :

>>> 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 课程时, 我发现这是一个最起码的足够解释 来建立理解, 并马上开始使用它。 特别是, 用打印写一个函数, 测试它, 然后转换成收益的技巧, 似乎对 Python 各级程序员都有效 。

生成关键字用于查点/字符,其中函数预期将返回一个输出。我想引用这个非常简单的例A:

# example A
def getNumber():
    for r in range(1,10):
        return r

以上函数只返回一次, 即使它被多次调用。 现在如果我们以收益率替换返回, 如例B :

# example B
def getNumber():
    for r in range(1,10):
        yield r

当第一次叫2时,它会返回1,当再次叫2时,3,4,然后它会递增到10。

虽然B的例子在概念上是真实的,但要用Python 3来称呼它,我们必须采取以下行动:


g = getNumber() #instance
print(next(g)) #will print 1
print(next(g)) #will print 2
print(next(g)) #will print 3

# so to assign it to a variables
v = getNumber()
v1 = next(v) #v1 will have 1
v2 = next(v) #v2 will have 2
v3 = next(v) #v3 will have 3

虽然很多答案都表明你为什么用一个产量来创建发电机, 但对于产量来说却有更多的用途。 做一个共同的习惯很容易, 这样可以让信息在两个代码区块之间传递。 我不再重复任何已经给出的关于使用产量来创建发电机的好例子 。

为了帮助理解下一个代码中的产值, 您可以用手指通过任何有产值的代码来追踪循环。 每次你的手指碰到产量的代码时, 您必须等待下一个或发送输入。 调用下一个代码时, 您可以跟踪代码, 直到你到达产量... 产值右侧的代码被评估并返回到调用者... 然后您等待。 当调用下一个代码时, 您会通过代码执行另一个循环。 但是, 您会注意到, 产值也可以用一个发送方式来使用。 发送时, 将会将一个调用器的值发送到产值中 。 如果发送了, 则会收到发送的值, 然后将其从左侧吐出... , 然后通过代码的跟踪直到您再次到达产量( 返回最后的值, 如下一个调用 ) 。

例如:

>>> def coroutine():
...     i = -1
...     while True:
...         i += 1
...         val = (yield i)
...         print("Received %s" % val)
...
>>> sequence = coroutine()
>>> sequence.next()
0
>>> sequence.next()
Received None
1
>>> sequence.send('hello')
Received hello
2
>>> sequence.close()

- 功能 - 返回。

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

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

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