我开始学习Python,我遇到过生成器函数,其中有yield语句。我想知道这些函数最擅长解决什么类型的问题。
当前回答
由于没有提到生成器的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上谈发电机
其他回答
简单的解释是: 考虑for语句
for item in iterable:
do_stuff()
很多时候,iterable中的所有项都不需要从一开始就存在,但可以在需要时动态生成。这在两种情况下都更有效
空间(您永远不需要同时存储所有项目)和 时间(迭代可能在需要所有项目之前完成)。
其他时候,你甚至不知道所有的项目提前。例如:
for command in user_input():
do_stuff_with(command)
你没有办法预先知道所有用户的命令,但如果你有一个生成器给你命令,你可以使用这样一个很好的循环:
def user_input():
while True:
wait_for_command()
cmd = get_command()
yield cmd
使用生成器,您还可以对无限序列进行迭代,这在迭代容器时当然是不可能的。
使用生成器的原因之一是为了使某些解决方案的解决方案更清晰。
另一种方法是一次处理一个结果,避免建立庞大的结果列表,否则无论如何都要分开处理。
如果你有这样一个fibonacci- to-n函数:
# 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项列表,每次只创建一个值。在使用列表版本时,情况并非如此,在列表版本中,将首先创建列表。
现实世界中的例子
假设你的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
正如你所看到的,我们的函数总是得到结果。如果使用关键字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()
请参阅PEP 255中的“动机”部分。
生成器的一个不太明显的用途是创建可中断函数,它允许您在不使用线程的情况下“同时”执行更新UI或运行多个作业(实际上是交错的)。
基本上避免回调函数时迭代输入维护状态。
请参阅这里和这里,了解使用生成器可以做什么。
推荐文章
- 证书验证失败:无法获得本地颁发者证书
- 当使用pip3安装包时,“Python中的ssl模块不可用”
- 无法切换Python与pyenv
- Python if not == vs if !=
- 如何从scikit-learn决策树中提取决策规则?
- 为什么在Mac OS X v10.9 (Mavericks)的终端中apt-get功能不起作用?
- 将旋转的xtick标签与各自的xtick对齐
- 为什么元组可以包含可变项?
- 如何合并字典的字典?
- 如何创建类属性?
- 不区分大小写的“in”
- 在Python中获取迭代器中的元素个数
- 解析日期字符串并更改格式
- 使用try和。Python中的if
- 如何在Python中获得所有直接子目录