重要的是列表理解会创建一个新列表。生成器创建一个可迭代的对象,当您使用这些位时,它将动态“过滤”源材料。
假设您有一个名为“ hugefile.txt”的2TB日志文件,并且想要以单词“ ENTRY”开头的所有行的内容和长度。
因此,您尝试通过编写列表理解来开始:
logfile = open("hugefile.txt","r")
entry_lines = [(line,len(line)) for line in logfile if line.startswith("ENTRY")]
这样会抓取整个文件,处理每一行,并将匹配的行存储在数组中。因此,此阵列最多可以包含2TB的内容。那会占用很多RAM,对于您的目的可能不切实际。
因此,我们可以使用生成器将“过滤器”应用于我们的内容。直到我们开始遍历结果之前,才实际读取任何数据。
logfile = open("hugefile.txt","r")
entry_lines = ((line,len(line)) for line in logfile if line.startswith("ENTRY"))
甚至没有从我们的文件中读取任何一行。实际上,假设我们想进一步过滤结果:
long_entries = ((line,length) for (line,length) in entry_lines if length > 80)
仍未读取任何内容,但是我们现在指定了两个生成器,它们将根据需要对数据起作用。
让我们将过滤后的行写到另一个文件中:
outfile = open("filtered.txt","a")
for entry,length in long_entries:
outfile.write(entry)
现在我们读取输入文件。随着for
循环继续请求其他行,long_entries
生成器要求生成器提供行entry_lines
,仅返回长度大于80个字符的行。然后,entry_lines
生成器从logfile
迭代迭代器读取文件。
因此,不是以完全填充列表的形式将数据“推送”到输出函数,而是为输出函数提供了一种仅在需要时才“拉”数据的方法。在我们的情况下,这要高效得多,但不够灵活。生成器是一种方式,一次通过。我们读取的日志文件中的数据会立即被丢弃,因此我们无法返回上一行。另一方面,完成数据后,我们不必担心保留数据。
[exp for x in iter]
只是糖list((exp for x in iter))
吗?还是执行差异?