你可以使用生成器函数做什么 (generator function python)

我开始学习 Python,我遇到了生成器函数,其中有一个 yield 语句。我想知道这些函数真正擅长解决哪些类型的问题。

我开始学习 Python,我遇到了生成器函数,其中有一个 yield 语句。我想知道这些函数真正擅长解决哪些类型的问题。

262

您可以将生成器视为返回多个项,就像它们返回一个列表一样,但不是一次返回所有项,而是逐个返回,生成器函数将暂停,直到请求下一个项。

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

生成器的另一个用途 (实际上是一样的) 是用迭代替换回调。在某些情况下,您希望函数执行大量工作,并偶尔向调用者报告。传统上,您会为此使用回调函数。您将此回调传递给工作函数,它将定期调用此回调。生成器方法是工作函数 (现在是一个生成器) 对回调一无所知,而只是在它想要报告回调函数的所有工作时产生。

例如,假设您编写了一个“文件系统搜索”程序。您可以完整地执行搜索,收集结果,然后一次显示一个结果。在显示第一个结果之前,必须收集所有结果,并且所有结果都将同时存储在内存中。或者,您可以在找到结果时显示结果,这将使内存效率更高,并且对用户更友好。后者可以通过将搜索功能传递给文件系统来完成。

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

big_list = list(the_generator)
94

使用生成器的原因之一是为了使某种解决方案更清晰。

另一种方法是一次处理一个结果,避免建立庞大的结果列表,而这些结果列表无论如何都会分开处理。

如果你有这样一个斐波那契函数:

# function version
def fibon(n):
    a = b = 1
    result = []
    for i in xrange(n):
        result.append(a)
        a, b = b, a + b
    return result

您可以更轻松地编写函数:

# generator version
def fibon(n):
    a = b = 1
    for i in xrange(n):
        yield a
        a, b = b, a + b

函数更清晰。如果你使用这样的函数:

for x in fibon(1000000):
    print x,

在这个例子中,如果使用生成器版本,整个 1000000 项目列表根本不会被创建,一次只创建一个值。

48

我发现这个解释消除了我的疑问。因为有可能不知道Generators的人也不知道yield

Return

return 语句是销毁所有局部变量并将结果值返回(返回)给调用者的地方。如果稍后调用相同的函数,该函数将获得一组新的变量。

Yield

这就是引入generators概念的地方,yield语句在function停止的地方恢复。

  def generate_integers(N):
    for i in xrange(N):
    yield i
    In [1]: gen = generate_integers(3)
    In [2]: gen
    <generator object at 0x8117f90>
    In [3]: gen.next()
    0
    In [4]: gen.next()
    1
    In [5]: gen.next()

这就是 Python 中returnyield语句之间的区别。

Yield 语句使函数成为生成器函数。

因此,生成器是创建迭代器的简单而强大的工具。它们的编写方式类似于常规函数,但是每当它们想要返回数据时,它们都会使用yield语句。每次调用 next () 时,生成器都会从它停止的地方恢复 (它记住所有数据值以及最后执行的语句)。

47

真实世界的例子

假设您的 MySQL 表中有 1 亿个域,并且您希望为每个域更新 Alexa 排名。

您需要的第一件事是从数据库中选择域名。

假设您的表名是domains,列名是domain

如果您使用SELECT domain FROM domains,它将返回 1 亿行,这将消耗大量内存。所以你的服务器可能会崩溃。

所以你决定批量运行这个程序。假设我们的批量大小是 1000。

在我们的第一批中,我们将查询前 1000 行,检查每个域的 Alexa 排名并更新数据库行。

在我们的第二批中,我们将处理接下来的 1000 行。在我们的第三批中,它将从 2001 到 3000,依此类推。

现在我们需要一个生成器函数来生成我们的批处理。

这是我们的生成器函数:

def ResultGenerator(cursor, batchsize=1000):
    while True:
        results = cursor.fetchmany(batchsize)
        if not results:
            break
        for result in results:
            yield result

如您所见,我们的函数保持yield结果。如果您使用关键字return而不是yield,那么整个函数将在到达 return 时结束。

return - returns only once
yield - returns multiple times

如果一个函数使用关键字yield,那么它就是一个生成器。

现在你可以像这样迭代:

db = MySQLdb.connect(host="localhost", user="root", passwd="root", db="domains")
cursor = db.cursor()
cursor.execute("SELECT domain FROM domains")
for result in ResultGenerator(cursor):
    doSomethingWith(result)
db.close()

本站系公益性非盈利分享网址,本文来自用户投稿,不代表码文网立场,如若转载,请注明出处

(69)
如何在COBOL中对PICX子句进行REDEFINE和执行算术运算
上一篇
如何使用一系列单元格使用“搜索( search_for”函数
下一篇

相关推荐

发表评论

登录 后才能评论

评论列表(51条)