小知识,大挑战!本文正在参与「程序员必备小知识」创作活动
最近要做一个数据源,当但是又不想随机生成,感觉随机生成的数据完全被没有真实数据集的特征那样明显。当时单纯的数据集毕竟是有限的,我又想它能够尽量多,最好是无限循环读取那种。
这里我们尝试用python实现。
假设文件里,有一定的数据,那么我们可以先就想办法读取一次数据。 由于python中打开的文件实际上是一个可迭代的对象(以行为单位迭代),所以我们可以直接通过读取并输出每一个数据(这里把输出到输出流看作是产出数据)。
with open('file',encoding='utf-8') as f:
for i in f:
print(i)
读到文件尾部就不会再产出数据了,所以我们还需要再对它做改进。
在python的itertools中,其实有一个这样的函数cycle,它可以让一个可迭代对象被重复的使用,像下面这样:
from itertools import cycle
with open('file',encoding='utf-8') as f:
for i in cycle(f):
print(i)
这样当f最后一个值被迭代出来之后,cycle会同时得到保留所有被迭代过的值的列表,然后循环读取列表,以重复让外部迭代。
从效果上来讲,是可以实现无限的迭代的,但是并不符合我们的要求。
因为我们当我们准备的文件很大的时候,会有个很大的问题: 占用内存过大。这是令人难以接受的。
但是如果我们反过来cycle的设计是因为有些可迭代对象是不可以重复的,所以需要保存。
而我们的数据其实是在文件里,文件中的数据本身就是保存过一份了。如果在内存中再保存一份就属于冗余了。
我们可以参考cycle的想法手动实现一个自己的迭代器。
不过现在有一个问题是:如何在f被读取到文件尾部时,从头再读取一遍?重新创建文件对象吗?
幸运的是,python中的文件对象和我们在c语言中的文件类似,支持随机访问,有一个文件指针或者说偏移位置。
我们把它设置到最开始,就可以重新读取了。 像下面这样
def cycle( filename, enconding):
with open(filename,encoding=enconding) as f:
while True:
yield from f
f.seek(0)
为了简洁,我们使用yield from 产出所有的数据行,然后回到起点,重新产出所有的数据行。
到这里我们就完成了使用生成器实现的循环文件流了,借助强大的生成器机制和文件自带的随机访问特性,实际上代码只用了4行。