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

the*_*orn 132 python python-2.7 python-3.x

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

fp = open('filename.txt')
while 1:
    line = fp.readline()
    if not line:
        break
    print line
Run Code Online (Sandbox Code Playgroud)

在Python 2.1之后,我们做了:

for line in open('filename.txt').xreadlines():
    print line
Run Code Online (Sandbox Code Playgroud)

在我们在Python 2.3中获得方便的迭代器协议之前,可以做到:

for line in open('filename.txt'):
    print line
Run Code Online (Sandbox Code Playgroud)

我见过一些使用更详细的例子:

with open('filename.txt') as fp:
    for line in fp:
        print line
Run Code Online (Sandbox Code Playgroud)

这是前进的首选方法吗?

[编辑]我得到了with语句确保关闭文件...但为什么不包含在文件对象的迭代器协议中?

Die*_*Epp 218

以下是首选的原因之一:

with open('filename.txt') as fp:
    for line in fp:
        print line
Run Code Online (Sandbox Code Playgroud)

我们都被CPython的相对确定性的垃圾收集引用计数方案所破坏.with如果他们使用其他方案来回收内存,那么其他假设的Python实现不一定会在没有块的情况下"足够快"地关闭文件.

在这样的实现中,如果您的代码打开文件的速度比垃圾收集器调用孤立文件句柄上的终结器更快,则可能会从操作系统中获得"打开太多文件"错误.通常的解决方法是立即触发GC,但这是一个讨厌的黑客,它必须由可能遇到错误的每个函数完成,包括库中的那些.什么样的恶梦.

或者你可以使用with块.

奖金问题

(如果只对问题的客观方面感兴趣,请立即停止阅读.)

为什么不包含在文件对象的迭代器协议中?

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

在内容层面上,这感觉是错误的,因为它使迭代器协议做两个单独的事情 - 遍历线关闭文件句柄 - 并且通常做一个看起来很简单的函数执行两个操作是个坏主意.在这种情况下,感觉特别糟糕,因为迭代器以准功能,基于值的方式与文件内容相关,但管理文件句柄是一项完全独立的任务.将这两者无形地拼接成一个动作对于阅读代码并使得更难以推理程序行为的人来说是令人惊讶的.

其他语言基本上得出了相同的结论.Haskell简单地与所谓的"懒惰IO"调情,它允许你迭代一个文件并在你到达流的末尾时自动关闭它,但是现在几乎普遍不鼓励在Haskell中使用惰性IO,而Haskell用户大多转向更明确的资源管理,如Conduit,其行为更像withPython中的块.

在技​​术层面上,你可能想要用Python中的文件句柄做一些事情,如果迭代关闭了文件句柄,这些事情也不会有效.例如,假设我需要迭代文件两次:

with open('filename.txt') as fp:
    for line in fp:
        ...
    fp.seek(0)
    for line in fp:
        ...
Run Code Online (Sandbox Code Playgroud)

虽然这是一个不太常见的用例,但考虑一下我可能刚刚在底部将三行代码添加到最初具有前三行的现有代码库这一事实.如果迭代关闭了文件,我将无法做到这一点.因此,将迭代和资源管理分开可以更容易地将代码块组合成更大的,有效的Python程序.

可组合性是语言或API最重要的可用性功能之一.


eum*_*iro 20

是,

with open('filename.txt') as fp:
    for line in fp:
        print line
Run Code Online (Sandbox Code Playgroud)

是要走的路.

它不是更冗长.它更安全.


Lie*_*yan 5

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

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

for line in with_iter(open('...')):
    ...
Run Code Online (Sandbox Code Playgroud)

在 Python 3.3 中,该yield from语句会使这更短:

def with_iter(iterable):
    with iterable as iter:
        yield from iter
Run Code Online (Sandbox Code Playgroud)

  • 调用函数 xreadlines.. 并将其放入名为 xreadlines.py 的文件中,我们将回到 Python 2.1 语法:-) (2认同)