在这种情况下使用发电机有什么好处?

时间:2011-05-28 13:46:09

标签: python generator

我正在从这张幻灯片中学习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

在这里使用生成器有什么好处?

5 个答案:

答案 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)

虽然您的示例可能有点简单以充分利用生成器,但我更喜欢使用生成器来封装任何序列数据的生成,其中还存在某种类型的数据过滤。它保留了“我正在处理的数据”代码与“我如何获取数据”代码分开。