我开始学习Python,我遇到过生成器函数,其中有yield语句。我想知道这些函数最擅长解决什么类型的问题。


当前回答

基本上避免回调函数时迭代输入维护状态。

请参阅这里和这里,了解使用生成器可以做什么。

其他回答

请参阅PEP 255中的“动机”部分。

生成器的一个不太明显的用途是创建可中断函数,它允许您在不使用线程的情况下“同时”执行更新UI或运行多个作业(实际上是交错的)。

生成器提供惰性求值。你可以通过对它们进行迭代来使用它们,或者显式地使用'for',或者隐式地将它传递给任何迭代的函数或构造。您可以将生成器视为返回多个项,就像它们返回一个列表一样,但它们不是一次返回所有项,而是一个接一个地返回它们,并且生成器函数将暂停,直到请求下一个项。

生成器很适合计算大量结果集(特别是涉及循环本身的计算),因为您不知道是否需要所有结果,或者您不想同时为所有结果分配内存。或者在发电机使用另一个发电机,或者消耗其他资源的情况下,如果发生得越晚越方便。

Another use for generators (that is really the same) is to replace callbacks with iteration. In some situations you want a function to do a lot of work and occasionally report back to the caller. Traditionally you'd use a callback function for this. You pass this callback to the work-function and it would periodically call this callback. The generator approach is that the work-function (now a generator) knows nothing about the callback, and merely yields whenever it wants to report something. The caller, instead of writing a separate callback and passing that to the work-function, does all the reporting work in a little 'for' loop around the generator.

For example, say you wrote a 'filesystem search' program. You could perform the search in its entirety, collect the results and then display them one at a time. All of the results would have to be collected before you showed the first, and all of the results would be in memory at the same time. Or you could display the results while you find them, which would be more memory efficient and much friendlier towards the user. The latter could be done by passing the result-printing function to the filesystem-search function, or it could be done by just making the search function a generator and iterating over the result.

如果您想查看后两种方法的示例,请参阅os.path.walk()(带有回调的旧文件系统遍历函数)和os.walk()(新的文件系统遍历生成器)。当然,如果你真的想收集一个列表中的所有结果,生成器方法转换为大列表方法是微不足道的:

big_list = list(the_generator)

当我们的web服务器充当代理时,我使用生成器:

客户端从服务器请求一个代理url 服务器开始加载目标url 服务器在获得结果后立即将结果返回给客户机

一堆东西。任何时候你想要生成一个项目序列,但又不想一次将它们全部“物化”到一个列表中。例如,你可以有一个简单的生成器,返回质数:

def primes():
    primes_found = set()
    primes_found.add(2)
    yield 2
    for i in itertools.count(1):
        candidate = i * 2 + 1
        if not all(candidate % prime for prime in primes_found):
            primes_found.add(candidate)
            yield candidate

然后你可以用它来生成后续质数的乘积:

def prime_products():
    primeiter = primes()
    prev = primeiter.next()
    for prime in primeiter:
        yield prime * prev
        prev = prime

这些都是相当简单的示例,但是您可以看到它对于处理大型(可能是无限的!)数据集是多么有用,而无需预先生成数据集,这只是比较明显的用途之一。

由于没有提到生成器的send方法,这里有一个例子:

def test():
    for i in xrange(5):
        val = yield
        print(val)

t = test()

# Proceed to 'yield' statement
next(t)

# Send value to yield
t.send(1)
t.send('2')
t.send([3])

它展示了向运行中的生成器发送值的可能性。下面视频中关于生成器的更高级课程(包括解释的yield,并行处理的生成器,逃避递归限制等)

David Beazley在PyCon 2014上谈发电机