我应该如何在Python中逐行读取文件?

我应该如何在Python中逐行读取文件?,python,python-3.x,python-2.7,Python,Python 3.x,Python 2.7,在史前时代(Python 1.4),我们做到了: 在Python 2.1之后,我们做到了: for line in open('filename.txt').xreadlines(): print line 在Python 2.3中使用方便的迭代器协议之前,我们可以: for line in open('filename.txt'): print line 我看到了一些使用更详细的示例: with open('filename.txt') as fp: for lin

在史前时代(Python 1.4),我们做到了:

在Python 2.1之后,我们做到了:

for line in open('filename.txt').xreadlines():
    print line
在Python 2.3中使用方便的迭代器协议之前,我们可以:

for line in open('filename.txt'):
    print line
我看到了一些使用更详细的示例:

with open('filename.txt') as fp:
    for line in fp:
        print line
这是未来的首选方法吗

[编辑]我知道with语句确保关闭文件。。。但是,为什么文件对象的迭代器协议中不包括这些内容呢?

是的

with open('filename.txt') as fp:
    for line in fp:
        print line
这是一条路要走


这不是更冗长。这样更安全

选择以下选项的原因只有一个:

with open('filename.txt') as fp:
    for line in fp:
        print line
我们都被CPython相对确定的垃圾收集引用计数方案宠坏了。如果Python的其他假想实现使用其他方案来回收内存,那么在没有
with
块的情况下,它们不一定会“足够快地”关闭文件

在这种实现中,如果代码打开文件的速度快于垃圾收集器对孤立文件句柄调用终结器的速度,则可能会从操作系统中得到“打开的文件太多”错误。通常的解决方法是立即触发GC,但这是一个令人讨厌的黑客行为,必须由每个可能遇到错误的函数(包括库中的函数)来完成。真是一场噩梦

或者您可以将
块一起使用

奖金问题 (如果你只对问题的客观方面感兴趣,现在就停止阅读。)

为什么在文件对象的迭代器协议中不包括这个

这是一个关于API设计的主观问题,所以我有两个部分的主观答案

从本质上讲,这感觉是错误的,因为它使迭代器协议执行两个独立的操作,即在行上迭代并关闭文件句柄,而让一个外观简单的函数执行两个操作通常是个坏主意。在这种情况下,感觉尤其糟糕,因为迭代器以准函数的、基于值的方式与文件内容相关,但管理文件句柄是一项完全独立的任务。将两者无形地挤压成一个动作,这让阅读代码的人感到惊讶,并使人们更难对程序行为进行推理

其他语言基本上得出了相同的结论。Haskell曾短暂尝试过所谓的“lazy IO”,它允许您迭代一个文件,并在到达流的末尾时自动关闭该文件,但现在几乎普遍不鼓励在Haskell中使用lazy IO,Haskell用户大多转向更显式的资源管理,如conduct,其行为更像Python中的
with

在技术层面上,您可能希望在Python中使用文件句柄来完成一些事情,如果迭代关闭文件句柄,这些事情将无法正常工作。例如,假设我需要在文件上迭代两次:

with open('filename.txt') as fp:
    for line in fp:
        ...
    fp.seek(0)
    for line in fp:
        ...
虽然这是一个不太常见的用例,但是考虑一下,我可能只是在底部添加了三行代码,这是一个原本有三条最前线的现有代码库。如果迭代关闭了文件,我就不能这样做。因此,将迭代和资源管理分开,可以更容易地将代码块组合到一个更大、工作正常的Python程序中


可组合性是语言或API最重要的可用性特征之一。

如果您被额外的行关闭,您可以使用如下包装函数:

def with_iter(iterable):
    with iterable as iter:
        for item in iter:
            yield item

for line in with_iter(open('...')):
    ...
在Python 3.3中,来自语句的yield将使它变得更短:

def with_iter(iterable):
    with iterable as iter:
        yield from iter

嗯,最后一个建议并不比前一个更冗长。它只是做了更多的工作(确保文件在您完成后关闭)。@azhrei它多了一行,所以客观上更详细。我明白您的意思,但我只是说,将苹果与苹果进行比较,您文章中的第二个建议还需要异常处理代码,以匹配最后一个选项的功能。所以在实践中它更冗长。我想这取决于上下文,最后两个选项中哪一个是最好的,真的。+1因为它解释了我在评论op时的“when;”-)即使使用替代实现,with处理程序也只会给那些以非常快的顺序打开数百个文件的程序带来问题。大多数程序都可以使用悬空的文件引用,没有问题。除非禁用它,否则GC最终会在某个时候启动并清除文件句柄
with
确实可以让您安心,因此这仍然是一种最佳做法。@DietrichEpp:也许“悬挂文件引用”不是正确的词,我的意思是不再可访问但尚未关闭的文件句柄。在任何情况下,GC在收集文件对象时都会关闭文件句柄,因此,只要您没有对文件对象的额外引用,并且没有禁用GC,并且没有连续快速打开许多文件,您就不可能因为没有关闭文件而打开“太多文件”。是的,这正是我的意思“如果代码打开文件的速度快于垃圾收集器对孤立文件句柄调用终结器的速度“。使用的更大原因是,如果不关闭文件,它不一定会立即被写入。请调用函数xreadlines。。然后把它放在一个名为xreadlines.py的文件中,我们回到Python 2.1语法:-)@thebjorn:也许吧,但是您所引用的Python 2.1示例在其他实现中对于未关闭的文件处理程序并不安全。从未关闭的文件处理程序中安全读取Python 2.1文件至少需要5行代码
def with_iter(iterable):
    with iterable as iter:
        yield from iter
f = open('test.txt','r')
for line in f.xreadlines():
    print line
f.close()