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


当前回答

这是关于产量的心理形象。

我想把一条线视为有堆叠(即使它不是用这种方式执行的)。

当调用一个普通函数时, 它会将其本地变量放入堆栈, 进行一些计算, 然后清除堆栈和返回。 其本地变量的值再也不会被看到 。

当它的代码开始运行时( 即函数被调用后, 返回一个生成对象, 其下一个( ) 方法随后被引用) , 它同样将其本地变量放在堆叠上, 并进行一段时间的计算 。 但是, 当它点击收益语句时, 在清理堆叠中的一部分并返回之前, 它会对其本地变量进行截图, 并将其存储在生成对象中 。 它还写下它目前位于代码中的位置( 即特定收益语句 ) 。

所以这是一种冷冻功能 发电机挂在了上面

当下一个 () 后被调用时, 它会从堆栈上取回函数的物品, 并重新激活它。 函数继续从剩余部分进行计算, 无视它刚刚在冷藏室里度过了一个永恒的时间 。

比较以下实例:

def normalFunction():
    return
    if False:
        pass

def yielderFunction():
    return
    if False:
        yield 12

当我们称第二个函数为第二个函数时, 它的行为与第一个函数非常不同。 收益声明可能无法达到, 但是如果它存在任何地方, 它会改变我们正在处理的东西的性质 。

>>> yielderFunction()
<generator object yielderFunction at 0x07742D28>

调用 powerFunction () 不运行它的代码, 而是让一个生成器出自代码 。 (也许用 powerFunction () 的前缀命名这种东西作为可读性是一个好主意 。)

>>> gen = yielderFunction()
>>> dir(gen)
['__class__',
 ...
 '__iter__',    #Returns gen itself, to make it work uniformly with containers
 ...            #when given to a for loop. (Containers return an iterator instead.)
 'close',
 'gi_code',
 'gi_frame',
 'gi_running',
 'next',        #The method that runs the function's body.
 'send',
 'throw']

gi_ code 和 gi_ frame 字段是存放冻结状态的地方。 使用 dir (.) 来探索它们, 我们可以确认上面的心理模型是可信的 。

其他回答

对于那些更喜欢最低限度工作实例的人来说,考虑一下这次交互式的Python会议:

>>> def f():
...   yield 1
...   yield 2
...   yield 3
... 
>>> g = f()
>>> for i in g:
...   print(i)
... 
1
2
3
>>> for i in g:
...   print(i)
... 
>>> # Note that this time nothing was printed

- 功能 - 返回。

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

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

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

以下是基于收益率的简单方法, 用来计算Fibonacci系列, 解释如下:

def fib(limit=50):
    a, b = 0, 1
    for i in range(limit):
       yield b
       a, b = b, a+b

当你把这个输入你的REPL,然后尝试把它称为, 你会得到一个神秘的结果:

>>> fib()
<generator object fib at 0x7fa38394e3b8>

这是因为向 Python 发出的产出信号 表明您想要创建一个生成器, 即一个根据需求产生价值的物体。

那么,你如何生成这些值?这要么直接通过下一个使用内置函数来实现,要么间接地通过将内置函数输入一个消耗值的构造来实现。

使用下个() 内置函数, 您可以直接引用. extext/ __ extext_ , 迫使生成器产生值 :

>>> g = fib()
>>> next(g)
1
>>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> next(g)
5

间接地,如果您为循环提供纤维、列表初始化器、图普特初始化器或其他任何期望产生/产生值的对象,您将“组装”生成器,直到它不再产生(并返回):

results = []
for i in fib(30):       # consumes fib
    results.append(i) 
# can also be accomplished with
results = list(fib(30)) # consumes fib

类似地,图普特首发器:

>>> tuple(fib(5))       # consumes fib
(1, 1, 2, 3, 5)

生成器与功能不同, 因为它很懒。 它通过保持本地状态, 并允许您在需要的时候恢复运行来达到这个目的 。

当你喊叫它的时候,

f = fib()

Python 编译函数, 遇到产出关键字, 只需返回生成对象。 似乎没有什么帮助 。

当您要求它生成第一个值时, 它会直接或间接地执行它发现的所有语句, 直到它遇到一个产量, 然后它会返回您提供的产量和暂停值。 对于一个更能证明这一点的例子, 让我们使用一些打印电话( 如果在 Python 2 上用打印“ text ” 代替 打印“ text ” ):

def yielder(value):
    """ This is an infinite generator. Only use next on it """ 
    while 1:
        print("I'm going to generate the value for you")
        print("Then I'll pause for a while")
        yield value
        print("Let's go through it again.")

现在,输入REPL:

>>> gen = yielder("Hello, yield!")

您现在有一个生成对象, 正在等待一个命令来生成一个值。 使用下一个对象并查看打印的内容 :

>>> next(gen) # runs until it finds a yield
I'm going to generate the value for you
Then I'll pause for a while
'Hello, yield!'

未引用的结果是打印的内容。 引用的结果是从产出中返回的内容。 现在再次调用 :

>>> next(gen) # continues from yield and runs again
Let's go through it again.
I'm going to generate the value for you
Then I'll pause for a while
'Hello, yield!'

生成器记得它被按产出值暂停, 然后从那里恢复。 下一则消息被打印, 并搜索收益声明以在它上再次暂停( 原因是同时循环 ) 。

还有一件事情要提: 产量的函数其实不一定要终止。我写了这样的代码:

def fib():
    last, cur = 0, 1
    while True: 
        yield cur
        last, cur = cur, last + cur

这样我就可以用在别的代码里了

for f in fib():
    if some_condition: break
    coolfuncs(f);

它确实有助于简化一些问题,使一些事情更容易处理。

我不太熟悉Python, 但我相信它和C#的传动屏障一样, 如果你熟悉这些。

关键的想法是, 编译者/ 解释者/ 不论做什么诡计, 就打电话者而言, 他们可以继续调用下一个 () , 并且它会继续返回数值 - 仿佛生成器方法被暂停。 现在显然你无法真正“ 暂停” 一种方法, 所以编译者可以建立一个状态机器, 以便您记住您目前的位置和本地变量等的外观 。 这比自己写一个转录器容易得多 。