9

我遇到了两个版本的代码,它们都可以完成相同的任务,但代码本身略有不同:

with open("file") as f:
   for line in f:
     print line

with open("file") as f:
   data = f.readlines() 
   for line in data:
     print line 

我的问题是,文件对象f是默认的列表data吗?如果不是,为什么第一块代码有效?哪个版本是更好的做法?

4

4 回答 4

11

Fileobject 不是list- 它是一个符合迭代器接口 ( docs ) 的对象。即它实现__iter__了返回迭代器对象的方法。该迭代器对象同时实现了__iter__next允许对集合进行迭代的方法。

碰巧File对象是它自己的迭代器(文档),意思是file.__iter__()返回self

两者for line in filelines = file.readlines()是等效的,因为如果用于获取/迭代文件中的所有行,它们会产生相同的结果。但是file.next() 缓冲文件中的内容(它提前读取)以加快读取过程,有效地将文件描述符移动到与最后一行结束的位置完全相同或更远的位置。这意味着如果您已经使用for line in file, 读取了一些行并且停止了迭代(您还没有到达文件的末尾)并且现在调用file.readlines()了 ,那么返回的第一行可能不是for在循环中迭代的最后一行之后的完整行。

使用for x in my_it时,解释器调用my_it.__iter__()。现在,该next()方法正在上一次调用返回的对象上调用,并且对于每个调用,它的返回值都被分配给x. 当next()raisesStopIteration时,循环结束。

注意:一个有效的迭代器实现应该确保一旦StopIteration被引发,它应该在所有后续调用中保持被引发next()

于 2013-10-01T16:36:44.500 回答
7

在这两种情况下,您都会逐行获取文件。方法不同。

使用您的第一个版本:

with open("file") as f:
   for line in f:
     print line

当您逐行遍历文件时,文件内容不会完全驻留在内存中(除非它是 1 行文件)。

open内置函数返回一个文件对象——而不是一个列表。该对象支持迭代;在这种情况下,返回单个字符串,这些字符串是文件中的每组字符,以回车符或文件结尾结尾。

您可以编写一个类似于幕后操作的循环for line in f: print line

with open('file') as f:
    while True:
        try:
            line=f.next()
        except StopIteration:
            break
        else:
            print line 

使用第二个版本:

with open("file") as f:
   data = f.readlines()    # equivelent to data=list(f)
   for line in data:
     print line

您正在使用文件对象 ( file.readlines() ) 的方法,该方法将整个文件内容作为单个行的列表读入内存。然后代码将遍历该列表。

您也可以编写一个类似的版本,突出显示引擎盖下的迭代器:

with open('file') as f:
    data=list(f)
    it=iter(data)
    while True:
        try:
            line=it.next()
        except StopIteration:
            break
        else:
            print line  

在您的两个示例中,您都使用for 循环来遍历序列中的项目。这些项目在每种情况下都是相同的(文件的各个行),但基础顺序不同。在第一个版本中,序列是一个文件对象;在第二个版本中,它是一个列表。如果您只想处理每一行,请使用第一个版本。如果您想要行列表,请使用第二个。

阅读 Ned Batchelder关于循环和迭代的优秀概述了解更多信息。

于 2013-10-01T16:36:41.330 回答
4

f是一个文件句柄,而不是一个列表。它是可迭代的。

于 2013-10-01T16:36:47.237 回答
0

文件是可迭代的。很多对象,包括列表都是可迭代的,这只是意味着它们可以在 for 循环中使用,以顺序产生一个对象来绑定 for 迭代器变量。

您的代码的两个版本都逐行完成迭代。第二个版本将整个文件读入内存并构造一个列表;第一个可能不会先读取整个文件。您可能更喜欢第二种的原因是您想在其他人修改文件之前关闭文件;如果文件非常大,第一个可能是首选。

于 2013-10-01T16:39:04.980 回答