我正在从这张幻灯片中学习Python的生成器:http://www.dabeaz.com/generators/Generators.pdf
有一个例子,可以这样描述:
你有一个名为log.txt
的日志文件,写一个程序来观察它的内容,如果有新行添加,打印它们。两种解决方案:
1. with generator:
import time
def follow(thefile):
while True:
line = thefile.readline()
if not line:
time.sleep(0.1)
continue
yield line
logfile = open("log.txt")
loglines = follow(logfile)
for line in loglines:
print line
2. Without generator:
import time
logfile = open("log.txt")
while True:
line = logfile.readline()
if not line:
time.sleep(0.1)
continue
print line
在这里使用生成器有什么好处?
答案 0 :(得分:5)
如果你拥有的只是一把锤子,那么一切看起来都像钉子一样
我几乎只是想用上面的引用回答这个问题。仅仅因为你并不意味着你需要一直。
但概念上,生成器版本将功能分开,follow函数用于在等待新输入时封装文件的连续读取。这使您可以使用所需的新行释放循环中的任何内容。在第二个版本中,从文件中读取并打印输出的代码与控制循环混合在一起。在这个小例子中,这可能不是真正的问题,但这可能是你想要考虑的事情。
答案 1 :(得分:1)
一个好处是能够通过调用.next()
来传递您的生成器(比如说不同的函数)并手动迭代。以下是初始生成器示例的略微修改版本:
import time
def follow(file_name):
with open(file_name, 'rb') as f:
for line in f:
if not line:
time.sleep(0.1)
continue
yield line
loglines = follow(logfile)
first_line = loglines.next()
second_line = loglines.next()
for line in loglines:
print line
首先,我使用上下文管理器(with
语句打开文件,该语句在完成后自动关闭文件,或者在异常时自动关闭文件)。接下来,在底部我已经演示了使用.next()
方法,允许您手动单步执行。如果您需要从简单的for item in gen
循环中打破逻辑,这有时非常有用。
答案 2 :(得分:1)
生成器函数的定义与普通函数类似,但只要需要生成一个值,它就会使用yield关键字而不是return。它的主要优点是它允许代码随着时间的推移生成一系列值,而不是一次计算它们并像列表一样发回它们。例如
# A Python program to generate squares from 1
# to 100 using yield and therefore generator
# An infinite generator function that prints
# next square number. It starts with 1
def nextSquare():
i = 1;
# An Infinite loop to generate squares
while True:
yield i*i
i += 1 # Next execution resumes
# from this point
# Driver code to test above generator
# function
for num in nextSquare():
if num > 100:
break
print(num)
返回将指定的值发送回其调用者,而Yield可以生成一系列值。当我们想迭代一个序列时,我们应该使用yield,但不想将整个序列存储在内存中。
答案 3 :(得分:0)
理想情况下,大多数循环大致具有以下形式:
for element in get_the_next_value():
process(element)
然而有时(如你的例子#2),循环实际上更复杂,因为你有时会得到一个元素,有时却没有。这意味着在没有元素的示例中,您混合了用于生成元素的代码以及用于处理它的代码。它在示例中没有显示得太清楚,因为生成下一个值的代码实际上并不太复杂,处理只是一行,但是第1个示例将这两个概念更清晰地分开。
一个更好的例子可能是从一个文件处理可变长度的段落,每个段落用空行分隔:尝试使用和不使用生成器编写代码,你应该看到好处。
答案 4 :(得分:0)
虽然您的示例可能有点简单以充分利用生成器,但我更喜欢使用生成器来封装任何序列数据的生成,其中还存在某种类型的数据过滤。它保留了“我正在处理的数据”代码与“我如何获取数据”代码分开。