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


当前回答

发电机可以使个别经过处理的物品立即得到处理(不必等待整个收集过程的处理),下面的例子说明了这一点。

import time

def get_gen():
    for i in range(10):
        yield i
        time.sleep(1)

def get_list():
    ret = []
    for i in range(10):
        ret.append(i)
        time.sleep(1)
    return ret


start_time = time.time()
print('get_gen iteration (individual results come immediately)')
for i in get_gen():
    print(f'result arrived after: {time.time() - start_time:.0f} seconds')
print()

start_time = time.time()
print('get_list iteration (results come all at once)') 
for i in get_list():
    print(f'result arrived after: {time.time() - start_time:.0f} seconds')

get_gen iteration (individual results come immediately)
result arrived after: 0 seconds
result arrived after: 1 seconds
result arrived after: 2 seconds
result arrived after: 3 seconds
result arrived after: 4 seconds
result arrived after: 5 seconds
result arrived after: 6 seconds
result arrived after: 7 seconds
result arrived after: 8 seconds
result arrived after: 9 seconds

get_list iteration (results come all at once)
result arrived after: 10 seconds
result arrived after: 10 seconds
result arrived after: 10 seconds
result arrived after: 10 seconds
result arrived after: 10 seconds
result arrived after: 10 seconds
result arrived after: 10 seconds
result arrived after: 10 seconds
result arrived after: 10 seconds
result arrived after: 10 seconds

其他回答

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

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

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

失败给了你一台发电机

def get_odd_numbers(i):
    return range(1, i, 2)
def yield_odd_numbers(i):
    for x in range(1, i, 2):
       yield x
foo = get_odd_numbers(10)
bar = yield_odd_numbers(10)
foo
[1, 3, 5, 7, 9]
bar
<generator object yield_odd_numbers at 0x1029c6f50>
bar.next()
1
bar.next()
3
bar.next()
5

正如你可以看到的那样,在第一种情况下,Foo同时持有完整的记忆列表。对于包含5个元素的清单来说,这不是什么大不了的事,但是如果你想列出500万个元素的清单,那又会怎样?这不仅仅是一个巨大的记忆食用器,在函数被调用的时候,它还要花费很多时间来构建这个功能。

在第二种情况下, 酒吧只给您一台发电机。 发电机是一个可循环的, 意思是您可以在循环中使用它, 等等, 但每个值只能存取一次。 所有值也并非同时存储在记忆中; 生成器的“ 成员” 对象, 上次您称之为“ 成员” 时, 它在循环中。 这样, 如果您使用一个可( 说) 的转号, 计数为500亿, 你不必一次数到500亿, 然后存储500亿的数值来进行计算。

再者,这是一个相当巧妙的例子,如果你真想数到500亿,你可能会使用滑板。 () :

这是发电机中最简单的使用实例。 正如您所说, 它可以用来写高效的变换, 使用产量将东西推到调用堆叠上, 而不是使用某种堆叠变量。 发电机也可以用于专门的树道, 以及各种其它方式 。

简单解答

当函数包含至少一个输出语句时,函数自动成为生成函数。当您调用生成函数时, python 在生成函数中执行代码,直到生成语句发生。 当您调用生成函数时, python 继续从冻结位置执行生成函数中的代码,直到生成语句发生反复发生。 生成函数执行代码,直到生成函数在没有生成语句的情况下运行结束。

基准基准基准基准基准基准基准

创建列表并返回它 :

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

@profile
def function():
    my_sum = 0
    my_values = my_range(1000000)
    for my_value in my_values:
        my_sum += my_value

function()

结果有:

Total time: 1.07901 s
Timer unit: 1e-06 s

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     9                                           @profile
    10                                           def function():
    11         1          1.1      1.1      0.0      my_sum = 0
    12         1     494875.0 494875.0     45.9      my_values = my_range(1000000)
    13   1000001     262842.1      0.3     24.4      for my_value in my_values:
    14   1000000     321289.8      0.3     29.8          my_sum += my_value



Line #    Mem usage    Increment  Occurences   Line Contents
============================================================
     9   40.168 MiB   40.168 MiB           1   @profile
    10                                         def function():
    11   40.168 MiB    0.000 MiB           1       my_sum = 0
    12   78.914 MiB   38.746 MiB           1       my_values = my_range(1000000)
    13   78.941 MiB    0.012 MiB     1000001       for my_value in my_values:
    14   78.941 MiB    0.016 MiB     1000000           my_sum += my_value

在飞行上生成值 :

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

@profile
def function():
    my_sum = 0
    
    for my_value in my_range(1000000):
        my_sum += my_value

function()

结果有:

Total time: 1.24841 s
Timer unit: 1e-06 s

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     7                                           @profile
     8                                           def function():
     9         1          1.1      1.1      0.0      my_sum = 0
    10
    11   1000001     895617.3      0.9     71.7      for my_value in my_range(1000000):
    12   1000000     352793.7      0.4     28.3          my_sum += my_value



Line #    Mem usage    Increment  Occurences   Line Contents
============================================================
     7   40.168 MiB   40.168 MiB           1   @profile
     8                                         def function():
     9   40.168 MiB    0.000 MiB           1       my_sum = 0
    10
    11   40.203 MiB    0.016 MiB     1000001       for my_value in my_range(1000000):
    12   40.203 MiB    0.020 MiB     1000000           my_sum += my_value

摘要摘要摘要

生成器函数需要稍多一点时间来执行, 而不是返回列表但少用内存的函数 。

以下是基于收益率的简单方法, 用来计算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 isPrimeNumber(n):
    print "isPrimeNumber({}) call".format(n)
    if n==1:
        return False
    for x in range(2,n):
        if n % x == 0:
            return False
    return True

def primes (n=1):
    while(True):
        print "loop step ---------------- {}".format(n)
        if isPrimeNumber(n): yield n
        n += 1

for n in primes():
    if n> 10:break
    print "wiriting result {}".format(n)

产出:

loop step ---------------- 1
isPrimeNumber(1) call
loop step ---------------- 2
isPrimeNumber(2) call
loop step ---------------- 3
isPrimeNumber(3) call
wiriting result 3
loop step ---------------- 4
isPrimeNumber(4) call
loop step ---------------- 5
isPrimeNumber(5) call
wiriting result 5
loop step ---------------- 6
isPrimeNumber(6) call
loop step ---------------- 7
isPrimeNumber(7) call
wiriting result 7
loop step ---------------- 8
isPrimeNumber(8) call
loop step ---------------- 9
isPrimeNumber(9) call
loop step ---------------- 10
isPrimeNumber(10) call
loop step ---------------- 11
isPrimeNumber(11) call

我不是Python开发者,但对我来说,它似乎保持了程序流程的位置,而下一个循环则从“当量”的位置开始。 它似乎正在等待着这个位置,就在那个位置之前,它正在向外回报一个价值,而下一次将继续工作。

这似乎是一个有趣和好的能力: