在这种情况下使用生成器有什么好处?

2 投票
5 回答
1182 浏览
提问于 2025-04-16 18:31

我正在通过这个幻灯片学习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 个回答

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)

使用“return”会把一个指定的值返回给调用它的地方,而“yield”则可以产生一系列的值。当我们想要逐个处理一系列数据,但又不想把整个序列都存储在内存里时,就应该使用“yield”。

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 循环中分离出来时。

5

如果你手里只有一把锤子,所有东西看起来都像钉子。

我几乎想用这句话来回答这个问题。仅仅因为你能做到,并不意味着你每次都需要这样做。

从概念上讲,生成器版本把功能分开了,跟随函数的作用是封装从文件中持续读取的过程,同时等待新的输入。这样你就可以在循环中随意处理新的一行内容。在第二个版本中,读取文件和打印的代码和控制循环混在一起。虽然在这个小例子中这可能不是个大问题,但这是你可能需要考虑的事情。

撰写回答