python3:生成器的多次读取

前言

生成器(generator)可以帮我们在传递大量数据时节省内存。

一般情况下,生成器只能被读取一次。

但是总有某种情况下,需要对生成器多次读取。

本文意在提供一种优雅的方式解决此类问题。

环境

python 3.6

正文

当要对一个生成器多次读值时,存在多种实现,比如 lamda。(印象中似乎有移动指针的方法)

但是这种代码并不优雅。

我们可以实现一个 迭代器协议的容器类。

这里举一个例子:
一个文件当中,每一行都存着一个数字,求出每一行数字的百分比。为了方便,这里拿一个列表来模拟文件。

详细代码如下:

# 生成器读取文件,这里使用列表模拟文件
def read_file_iter(file):
    # with open ...
    for line in file:
        yield int(line)


# 计算百分比,方法体中读取了两次生成器。一次是 sum,一次是 for。生成器在 sum 操作后便已经空了。
def cal_percent(numbers):
    total = sum(numbers)
    results = []
    for num in numbers:
        results.append(num / total * 100)
    return results


# 使用容器类实现迭代读取文件,这里依然使用列表模拟文件,略去打开文件的步骤
class ReadFile():
    def __init__(self, data_path):
        self.data_path = data_path

    # 每次轮询该对象时,均会生成一个新的生成器
    def __iter__(self):
        # with open ...
        for line in self.data_path:
            yield line


if __name__ == "__main__":
    numbers = [10, 100, 1000]

    # ------------------
    # 在需要对生成器多次读值时,普通的生成器写法不再合适。
    # 可以实现一种 迭代器协议的容器类 来满足这种需要对生成器多次读值的要求。
    # 下列 【情况一】 为普通的迭代写法,【情况二】为实现 迭代器协议的容器类
    # ------------------

    # 【情况一】:
    # 这里的结果是空。
    # 因为 read_numbers 是一个生成器。
    # 在 cal_percent 中 sum 方法和 for 循环均是对该生成器的读取。而生成器只能被读取一次
    read_numbers = read_file_iter(numbers)
    results = cal_percent(read_numbers)
    print(results)



    # 【情况二】
    # 这里的结果是正常的。
    # read_numbers 仅仅是一个不包含实际数据的对象
    # 在 cal_percent 中 sum 方法和 for 循环读取该对象时,该对象会分别返回两个独立的生成器。因此工作正常。
    read_numbers = ReadFile(numbers)
    results = cal_percent(read_numbers)
    print(results)

扩展

参考

你可能感兴趣的:(python3:生成器的多次读取)