【问题标题】:Preferred file line by line read idiom in PythonPython中的首选文件逐行读取习语
【发布时间】:2013-08-23 00:04:38
【问题描述】:

感觉几乎每次在 Python 中读取文件时,我想要的是:

with open("filename") as file_handle:
    for line in file_handle:
        #do something

这真的是首选成语吗?这让我有点恼火,这双缩进所有文件读取逻辑。有没有办法将此逻辑折叠成一行或一层?

【问题讨论】:

  • 是的,这是首选方式。据我所知,没有任何方法可以在不丢失with 语句的情况下折叠它。
  • 您始终可以明确关闭文件:fobj = open(...) for line in fobj: #do something fobj.close()。您可以在高度缩进的代码中使用它。
  • 使用2 作为标签深度而不是PEP8 4
  • @MattH 按照这个逻辑,我们可以完全取消缩进。希望您考虑一下这种疯狂的谈话,因为这种逻辑不是很有说服力。占用大量空间的每个缩进级别也有好处,它不鼓励重嵌套和/或非常长的行(无论前面有多少空格,这往往很难理解),并且突出显示每个缩进级别,这很重要,因为这些级别对应于控制流构造等重要事物。
  • 这里的关键是,是的,这是首选的成语。处理缩进太深的情况的首选方法是将某些内容分解到函数中。因此,即使您确实在这里提出了一个没有人想到的更好的解决方案,它仍然不会是惯用的解决方案。

标签: python file-io idioms


【解决方案1】:

简而言之,不,如果你想保持完全相同的语义。

【讨论】:

  • 最好说明一下这些语义是什么以及为什么它们是我们想要的。
【解决方案2】:

对于简单的情况,是的,两级 withfor 是惯用的。

对于缩进成为问题的情况,这里与 Python 中的其他任何地方一样,惯用的解决方案是找到一些东西以将其分解为函数。


您可以编写包装器来帮助解决这个问题。例如,这里有一个简单的方法来解决您使用with 处理的一些问题(例如,即使在最好的情况下,文件在您完成循环后仍然存在,直到范围结束 - 这可能是几天后,或者永远不会,如果范围是主事件循环或生成器或其他东西......):

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

for line in with_iter(open("filename")):
    # do something

for line in with_iter(open("other_filename")):
    # do something else

当然,它并不能解决所有问题。 (详情请参阅this ActiveState recipe。)

如果您知道它可以满足您的需求,那就太好了。如果您不了解这些差异……请坚持惯用语;这是惯用的。


那么,如何重构代码?最简单的方法通常是将循环体变成一个函数,因此您可以使用map 或理解:

def do_with_line(line):
    return line

with open("filename") as f:
    process = [do_with_line(line) for line in f]

但如果问题是for 上方或下方的代码太深,则必须在不同级别进行重构。

【讨论】:

  • 除了这个包装器和直接使用open()有同样的问题。资源管理变得不确定,只是现在它依赖于正在完成的生成器而不是正在完成的文件(值得注意的是,循环体中的异常不会触发文件的__exit__)。跨度>
  • @delnan:不,它没有。比如直接用openfor line in file做完后,文件还是打开的;对于with_iter,它不是。请参阅 ActiveState 配方,以及导致它的关于 python 想法的长时间讨论,因为这里有太多要讨论的内容。
  • 当然,它有一些相同的问题,解决了其他问题,并引入了一些新问题。 send_async 用例很有趣,但在这里不适用,因为文件被急切地使用。但正如我所说,如果循环过早结束,这并不能确保及时完成(例外,breakreturn)。如果它是一个异常并且捕获它的代码很粗心(IIRC 通过将回溯存储在局部变量中),它会创建一个引用循环,使生成器至少在下一个循环收集之前保持活动状态,或者 forever 在没有 PEP 442 的 CPython 中(仅在 v3.4 中添加)。
  • @delnan:即使我们这里的人都是一样的,即使网上还没有记录,SO cmets 是一种可怕的格式,无法就此进行长时间的讨论,所以我不会在这里重述整个事情。
  • @delnan:我重写了答案,以更清楚地说明这只是在某些情况下您可以提供帮助的示例。答案的重点仍然是前两句话。
【解决方案3】:

如果单个缩进会减少对您的刺激,您总是可以这样做:

with open("filename") as file_handle:
    fle = file_handle.read()

但是要小心大文件,因为在吞下整个文件后,它会进入您机器的内存。如果这样做,您可以实现单次缩进,并且仍然可以逐行迭代:

with open("filename") as file_handle:
    fle = file_handle.readlines()

文件中的行将被放置在列表中,每行都在单独的元素中,然后您可以像这样迭代它:

for ln in fle:
    #do something with ln here, it contain one line from your file

还是要小心大文件!因为这一切都是在内存中完成的。

【讨论】:

  • 它是一个大字符串,而不是一系列行......
【解决方案4】:

明确一点:

@我自己,这当然是成语!成语中的 with/for 行提供了几个好处:

  • 它会在出现错误时自动关闭文件。
  • 它逐块读取文件,限制内存使用。
  • 用途广泛;其他编码人员会立即理解。

【讨论】:

    【解决方案5】:

    是的,这绝对是惯用的 Python。

    您不应该被多层次的缩进所困扰。当然,这不是它发生的唯一方法,例如

    if condition:
        for x in sequence:
            #do something with x
    

    如果缩进级别变得太大,是时候重构为多个函数了。我最喜欢 Python 的一件事是它减少了分解事物的摩擦。

    with open("filename") as file_handle:
        result = do_something(file_handle)
    

    【讨论】:

      猜你喜欢
      • 2019-04-16
      • 1970-01-01
      • 1970-01-01
      • 2014-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-26
      • 2017-08-29
      相关资源
      最近更新 更多