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链接。


当前回答

输出是函数的返回元素。 区别在于, 产出元素将函数转换成生成器。 生成器的行为就像一个函数, 直到某种“ 归属 ” 。 生成器停止直到下一次调用, 并且从与开始的完全相同的点继续。 您可以通过调用列表( 生成器 () ) 获得一个序列中所有“ 属性” 值的序列。

其他回答

产出关键字简化为两个简单的事实:

如果编译者在函数内的任何地方检测到产出关键字,则该关键字函数不再通过返回语句返回。相反,它会立即返回一个称为生成器的懒惰的“待决列表”对象。“生成器”是可循环的。什么是可循环的?它像列表或设置或范围或编辑视图一样,带有按一定顺序访问每个元素的内置协议。

简言之: 最常见的情况是, 发电机是一个懒惰的、 递增的等待列表, 并且产出语句允许您使用函数符号来编程生成器应该逐渐吐出的列表值。 此外, 高级用法允许您使用发电机作为共程( 见下文 ) 。

generator = myYieldingFunction(...)  # basically a list (but lazy)
x = list(generator)  # evaluate every element into a list

   generator
       v
[x[0], ..., ???]

         generator
             v
[x[0], x[1], ..., ???]

               generator
                   v
[x[0], x[1], x[2], ..., ???]

                       StopIteration exception
[x[0], x[1], x[2]]     done

基本上, 只要遇到产出语句, 函数就会暂停并保存状态, 然后根据 Python 传动协议( 在某些合成结构中, 类似反复呼叫下一个( ) 的循环, 并捕捉一个停止作用的例外等) , 发出“ 列表中的下一个返回值 ” 。 您可能遇到过带有生成表达式的生成器; 生成函数更强大, 因为您可以将参数反馈到暂停的生成器功能中, 使用它们来实施 comutines 。 稍后会更多 。


基本示例(“清单”)

我们来定义一个函数,它就像 Python 的射程。 调用 makeRange(n) returns a Generator:

def makeRange(n):
    # return 0,1,2,...,n-1
    i = 0
    while i < n:
        yield i
        i += 1

>>> makeRange(5)
<generator object makeRange at 0x19e4aa0>

要强制生成器立即返回其未完成的值, 您可以将它传送到列表 () (就像您可以任意使用 ) :

>>> list(makeRange(5))
[0, 1, 2, 3, 4]

比较“仅返回列表”的示例

上述例子可视为仅仅是创建一份清单,并附在后面并返回:

# return a list                  #  # return a generator
def makeRange(n):                #  def makeRange(n):
    """return [0,1,2,...,n-1]""" #      """return 0,1,2,...,n-1"""
    TO_RETURN = []               # 
    i = 0                        #      i = 0
    while i < n:                 #      while i < n:
        TO_RETURN += [i]         #          yield i
        i += 1                   #          i += 1
    return TO_RETURN             # 

>>> makeRange(5)
[0, 1, 2, 3, 4]

不过,有一个重大差别;见最后一节。


您如何使用发电机

所有发电机都是易变的, 所以它们经常被这样使用:

#                  < ITERABLE >
>>> [x+10 for x in makeRange(5)]
[10, 11, 12, 13, 14]

要对发电机有更好的感觉,您可以玩过工具模块( 一定要使用链。 来自_ irightable, 而不是当必要时使用链子 ) 。 例如, 您甚至可以使用生成器来实施无限长的懒惰列表, 比如 ltertools. counts () 。 您可以执行您自己的除法列表( 可认证的) : zip( 计数 ) , 或使用一段时间内生成关键字来这样做 。

请注意: 发电机实际上可以用于更多的事情, 比如实施 comotines 或非 确定性编程或其他优雅的东西。 然而, 我在此介绍的“ 懒惰列表” 观点是您最常用的 。


幕后幕后

这就是“ Python 迭代协议” 的原理。 也就是说, 当您做列表( makeRange(5)) 时会发生什么 。 这就是我前面描述的“ 懒惰、 递增列表 ” 。

>>> x=iter(range(5))
>>> next(x)  # calls x.__next__(); x.next() is deprecated
0
>>> next(x)
1
>>> next(x)
2
>>> next(x)
3
>>> next(x)
4
>>> next(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

内建函数的下一个 () 只是调用对象 ._next__ () 函数, 这是“ 电路协议” 的一部分, 并在所有迭代器中找到 。 您可以手动使用下一个( ) 函数( 和迭代协议的其他部分) 来执行奇特的东西, 通常以降低可读性为代价, 所以尽量避免这样做...


锥体

圆柱形示例:

def interactiveProcedure():
    userResponse = yield makeQuestionWebpage()
    print('user response:', userResponse)
    yield 'success'

coroutine = interactiveProcedure()
webFormData = next(coroutine)  # same as .send(None)
userResponse = serveWebForm(webFormData)

# ...at some point later on web form submit...

successStatus = coroutine.send(userResponse)

comotine (generations 通常接受通过 产出 关键字输入输入 , 例如, 下一个 Input = 产生下一个输出, 作为一种双向通信形式) 基本上是允许暂停自己和请求输入的计算( 例如它下一步应该做什么 ) 。 当 comotine 暂停自己( 当运行中的 comotine 最终会点击 产出 关键字时) , 计算会暂停, 控制会被倒回“ 调” 函数( 要求下一个计算值的框架 ) 。 暂停的发电机/ 库外 仍然暂停, 直到另一个函数( 可能是一个不同的函数/ 文本) 启用后, 请求下一个值( 通常通过输入数据将暂停的逻辑内部输入到 comutine 的代码 ) 。

您可以将皮延共程视为懒惰的递增待决列表, 下一个元素不仅取决于先前的计算, 而且还取决于输入, 您可以选择在生成过程中注射 。


贫提亚e

通常,大多数人不会关心以下的区别,可能想在这里停止阅读。

在 Python-speak 中, 迭代是“ 理解“ 循环概念” 的任何物体, 如列表[ 1, 2, 3] , 转动器是请求循环的具体实例, 如 [ 1, 2, 3,. . _ eter __ () 。 生成器与任何迭代器完全相同, 但它的写法除外( 带有函数语法 ) 。

当您从列表中请求一个迭代器时, 它会创建一个新的迭代器。 但是, 当您从一个迭代器中请求一个迭代器( 您很少会这样做 ) 时, 它只会给您一个副本 。

因此,在不可能的情况下,你没有 做这样的事情...

> x = myRange(5)
> list(x)
[0, 1, 2, 3, 4]
> list(x)
[]

... 然后记住发电机是一个迭代器, 也就是说, 它是一次性使用。 如果您想要再使用它, 您应该再次调用 MyRange (...) 。 如果您需要使用结果两次, 将结果转换为列表, 并存储在变量 x = 列表( MyRange (5)) 中。 那些绝对需要克隆生成器的人( 例如, 那些正在做可怕的黑客化元程序设计的人) 可以使用它的工具.tee( 仍然在 Python 3 中工作) , 如果绝对需要的话, 因为可复制的迭代器 Python PEP 标准建议已被推迟 。

又一个TRL;DR

列表中的迭代器 : 下一个 () 返回列表的下一个元素 。

迭代生成器: 下一个 () 将计算苍蝇上的下一个元素( 执行代码)

您可以通过下拨“无论流量如何复杂”,将产出/生成器视为手动运行外部控制流的一种方式(如继续循环一步),然后调用该输出/生成器作为手动运行外部控制流的一种方式。

注意 : 生成器不是一个正常的函数。 它会像本地变量( stack) 一样记住先前的状态 。 请参看其他答案或文章以详细解释 。 生成器只能重复一次 。 您可以不生产, 但不会是那么好, 所以它可以被视为“ 非常好” 的语言糖 。

收益率和返回一样, 它会返回任何您告诉它的东西( 作为生成器 ) 。 区别在于下次您调用生成器时, 执行从最后一次调用开始到收益语句 。 与返回不同的是, 当收益发生时, 堆叠框架不会被清理, 但是控制会被转回调回调用方, 因此下次调用函数时, 它的状态将会恢复 。

在您的代码中,函数获取_child_camedates 的动作就像一个迭代器,这样当您扩展列表时,它会一次在新列表中添加一个元素 。

列表。extendend calls a plerator until it's fulled it's explator until. 如果是您所贴的代码样本, 只需将图普还给列表, 并附加到列表中, 就会更加清楚 。

- 功能 - 返回。

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

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

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

输出是函数的返回元素。 区别在于, 产出元素将函数转换成生成器。 生成器的行为就像一个函数, 直到某种“ 归属 ” 。 生成器停止直到下一次调用, 并且从与开始的完全相同的点继续。 您可以通过调用列表( 生成器 () ) 获得一个序列中所有“ 属性” 值的序列。