我开始学习Python,我遇到过生成器函数,其中有yield语句。我想知道这些函数最擅长解决什么类型的问题。
当前回答
这里有一些很好的答案,但是,我也推荐完整阅读Python函数式编程教程,它有助于解释生成器的一些更有效的用例。
特别有趣的是,现在可以从生成器函数外部更新yield变量,因此可以用相对较少的工作创建动态和交织的协程。 更多信息请参见PEP 342:通过增强型生成器的协程。
其他回答
我最喜欢的用法是“过滤”和“减少”操作。
假设我们正在读取一个文件,并且只想要以“##”开头的行。
def filter2sharps( aSequence ):
for l in aSequence:
if l.startswith("##"):
yield l
然后,我们可以在适当的循环中使用生成器函数
source= file( ... )
for line in filter2sharps( source.readlines() ):
print line
source.close()
reduce的例子类似。假设我们有一个文件,其中我们需要定位<Location>…< / >位置线。[不是HTML标签,而是恰好看起来像标签的行。]
def reduceLocation( aSequence ):
keep= False
block= None
for line in aSequence:
if line.startswith("</Location"):
block.append( line )
yield block
block= None
keep= False
elif line.startsWith("<Location"):
block= [ line ]
keep= True
elif keep:
block.append( line )
else:
pass
if block is not None:
yield block # A partial block, icky
同样,我们可以在一个合适的for循环中使用这个生成器。
source = file( ... )
for b in reduceLocation( source.readlines() ):
print b
source.close()
其思想是,生成器函数允许我们过滤或减少序列,每次生成一个值的另一个序列。
请参阅PEP 255中的“动机”部分。
生成器的一个不太明显的用途是创建可中断函数,它允许您在不使用线程的情况下“同时”执行更新UI或运行多个作业(实际上是交错的)。
你可以使用生成器的一个实际例子是,如果你有某种形状,你想要遍历它的角、边或其他地方。对于我自己的项目(源代码在这里),我有一个矩形:
class Rect():
def __init__(self, x, y, width, height):
self.l_top = (x, y)
self.r_top = (x+width, y)
self.r_bot = (x+width, y+height)
self.l_bot = (x, y+height)
def __iter__(self):
yield self.l_top
yield self.r_top
yield self.r_bot
yield self.l_bot
现在我可以创建一个矩形,并在它的角上循环:
myrect=Rect(50, 50, 100, 100)
for corner in myrect:
print(corner)
除了__iter__,你可以有一个方法iter_corners,并在myrect.iter_corners()中使用for corner来调用它。使用__iter__更优雅,因为我们可以在for表达式中直接使用类实例名。
简单的解释是: 考虑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
使用生成器,您还可以对无限序列进行迭代,这在迭代容器时当然是不可能的。
现实世界中的例子
假设你的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()
推荐文章
- 当使用代码存储库时,如何引用资源的相对路径
- 如何在Flask-SQLAlchemy中按id删除记录
- 在Python中插入列表的第一个位置
- Python Pandas只合并某些列
- 如何在一行中连接两个集而不使用“|”
- 从字符串中移除前缀
- 代码结束时发出警报
- 如何在Python中按字母顺序排序字符串中的字母
- 在matplotlib中将y轴标签添加到次要y轴
- 如何消除数独方块的凹凸缺陷?
- 为什么出现这个UnboundLocalError(闭包)?
- 使用Python请求的异步请求
- 如何检查一个对象是否是python中的生成器对象?
- 如何从Python包内读取(静态)文件?
- 如何计算一个逻辑sigmoid函数在Python?