【问题标题】:Is close() necessary when using iterator on a Python file object [duplicate]在 Python 文件对象上使用迭代器时是否需要 close() [重复]
【发布时间】:2010-12-22 09:31:41
【问题描述】:

执行以下操作并且不显式处理文件对象并调用其close() 方法是不好的做法吗?

for line in open('hello.txt'):
    print line

注意 - 这适用于还没有 with 语句的 Python 版本。

我问,因为 Python 文档似乎建议这样做:-

f = open("hello.txt")
try:
    for line in f:
        print line
finally:
    f.close()

这似乎比必要的更冗长。

【问题讨论】:

    标签: python file iterator


    【解决方案1】:

    实际上,文件在garbage collected时会被关闭。有关其工作原理的更多信息,请参阅this question。

    仍然建议您使用try/finally 块或with 语句。如果在使用文件对象的方法之一时出现异常,则引用将存储在回溯中(存储为全局变量),直到您清除它或发生另一个异常。

    因此,依靠垃圾收集来为您关闭文件是不好的。

    此外,如果您已写入文件,则无法保证在文件关闭或刷新之前所做的更改会保存到文件中。

    【讨论】:

    • +1 其余答案只是引用文档,您对使用 try/finally 给出了很好的解释。
    【解决方案2】:

    是的,否则你可能会泄漏资源。

    来自Python docs:

    完成文件后,调用f.close() 将其关闭并释放打开文件占用的所有系统资源。

    当程序退出时,这会发生在你身上,但否则 Python 会保留它不再需要的资源。

    【讨论】:

      【解决方案3】:

      奇怪的是,在本主题中关于释放系统资源重要性的所有讨论中,没有人提到在我看来确定性关闭文件的一个明显更重要的原因:以便可以再次打开它。

      当然,在某些情况下它并不重要。如果文件对象超出范围或被删除,则底层文件将被关闭。 (何时关闭取决于您使用的 Python 的具体实现。)这通常会足够好 - if 您确切知道文件变量何时超出范围,并且如果你知道你不在乎文件是否被确定性关闭。

      但是,当with 语句存在时,您为什么还要为这种分析而烦恼呢?

      【讨论】:

        【解决方案4】:

        在处理文件时总是需要关闭,将打开的文件句柄留在各处并不是一个好主意。当文件对象被垃圾回收时,它们最终将被关闭,但您不知道何时会关闭,同时您将通过持有不再需要的文件句柄来浪费系统资源。

        如果您使用的是 Python 2.5 及更高版本,则可以使用 with 语句自动为您调用 close():

        from __future__ import with_statement # Only needed in Python 2.5
        with open("hello.txt") as f:
            for line in f:
                print line
        

        这与您的代码具有相同的效果:

        f = open("hello.txt")
        try:
            for line in f:
                print line
        finally:
            f.close()
        

        with 语句是对 C++ 中常用的Resource Acquisition Is Initialization 成语的直接语言支持。它允许安全使用和清理各种资源,例如它可以用于始终确保关闭数据库连接或始终释放锁,如下所示。

        mylock = threading.Lock()
        with mylock:
            pass # do some thread safe stuff
        

        【讨论】:

        • 在小型、短时间运行的脚本中,谨慎的做法是权衡代码的清晰度与一个打开的文件在短时间内挂起。
        • with 语句很简单,很难证明不使用它是为了清楚起见。
        • 有时你的文件打开模式不能使用'with'来实现。例如,我现在正在编写一个脚本,在其中打开大约十几个文件,从标准输入中读取,将每一行发送到适当的文件,跟踪每个输出文件的行数,在 5000 处关闭它并打开一个新文件一个递增的后缀。
        【解决方案5】:

        不,我不认为更长的习语是必要的,原因如下:

        我为模式'for\s+.*\s+in\s+open\(' grepped /usr/lib/python2.6/ 发现很多例子

        for line in open('hello.txt'):
            print line
        

        到目前为止,

        的实例为零
        f = open("hello.txt")
        try:
            for line in f:
                print line
        finally:
            f.close()
        

        请参阅下面的标准库中使用 for ... in open 习语的文件列表。

        这自然会引出一个问题:如果 Python 开发人员接受 标准库,我们如何通过使用某些东西来改进任何东西 如果我们的代码依赖于标准库,我们自己的代码会有什么不同?

        我认为答案是,更长的成语并没有改善任何东西。

        我也跑了

        #!/usr/bin/env python
        try:
            for i,line in enumerate(open('a')):
                print line
                raw_input()
                if i==5:
                    break
        except Exception:
            pass
        
        raw_input()
        

        并检查/proc/PID/fd 文件描述符何时关闭。 看来,当您跳出 for 循环时,该文件已为您关闭。

        根据这些实验,我不相信长期 try...finally...close 成语是必要的。

        这是 grep 的结果:

        /usr/lib/python2.6/dist-packages/NvidiaDetector/nvidiadetector.py:89:tempList = [ x.strip() for x in open(obsolete).readlines() ]
        /usr/lib/python2.6/dist-packages/rpy_io.py:49:for line in open(file).readlines():
        /usr/lib/python2.6/dist-packages/setuptools/command/easy_install.py:1376:for line in open(self.filename,'rt'):
        /usr/lib/python2.6/dist-packages/GDebi/DscSrcPackage.py:47:for line in open(file):
        /usr/lib/python2.6/dist-packages/aptsources/distinfo.py:220:[x.strip() for x in open(value)])
        /usr/lib/python2.6/dist-packages/DistUpgrade/DistUpgradeCache.py:989:for line in open("/proc/mounts"):
        /usr/lib/python2.6/dist-packages/DistUpgrade/DistUpgradeAufs.py:100:for line in open("/proc/mounts"):
        /usr/lib/python2.6/dist-packages/DistUpgrade/DistUpgradeAufs.py:205:for line in open("/proc/mounts"):
        /usr/lib/python2.6/dist-packages/DistUpgrade/distinfo.py:220:[x.strip() for x in open(value)])
        /usr/lib/python2.6/dist-packages/DistUpgrade/DistUpgradeViewKDE.py:826:for c in open(sys.argv[2]).read():
        /usr/lib/python2.6/dist-packages/DistUpgrade/DistUpgradeConfigParser.py:45:items = [x.strip() for x in open(p)]
        /usr/lib/python2.6/dist-packages/DistUpgrade/DistUpgradeQuirks.py:684:for line in open(cpuinfo):
        /usr/lib/python2.6/dist-packages/DistUpgrade/DistUpgradeQuirks.py:692:for line in open("/proc/mounts"):
        /usr/lib/python2.6/dist-packages/DistUpgrade/DistUpgradeQuirks.py:726:for line in open("/etc/fstab"):
        /usr/lib/python2.6/dist-packages/DistUpgrade/DistUpgradeQuirks.py:762:for line in open(fstab):
        /usr/lib/python2.6/dist-packages/DistUpgrade/DistUpgradeQuirks.py:801:for line in open("/etc/fstab"):
        /usr/lib/python2.6/dist-packages/DistUpgrade/DistUpgradeQuirks.py:874:for line in open(XORG):
        /usr/lib/python2.6/dist-packages/DistUpgrade/DistUpgradeQuirks.py:939:for line in open(os.path.join(modaliasesdir,filename)):
        /usr/lib/python2.6/dist-packages/DistUpgrade/DistUpgradeController.py:1307:for line in open(template):
        /usr/lib/python2.6/dist-packages/DistUpgrade/xorg_fix_proprietary.py:23:for raw in open(xorg_source):
        /usr/lib/python2.6/dist-packages/DistUpgrade/xorg_fix_proprietary.py:58:for line in open(xorg):
        /usr/lib/python2.6/dist-packages/DistUpgrade/xorg_fix_proprietary.py:82:for line in open(xorg):
        /usr/lib/python2.6/dist-packages/jockey/oslib.py:377:for line in open(self.apt_jockey_source):
        /usr/lib/python2.6/dist-packages/jockey/oslib.py:393:for line in open(f):
        /usr/lib/python2.6/dist-packages/jockey/backend.py:651:for line in open(path):
        /usr/lib/python2.6/dist-packages/jockey/detection.py:277:for line in open(alias_file):
        /usr/lib/python2.6/dist-packages/jockey/detection.py:597:for l in open(os.path.join(path, 'uevent')):
        /usr/lib/python2.6/dist-packages/apt/cdrom.py:83:for line in open(fname):
        /usr/lib/python2.6/dist-packages/problem_report.py:1119:for line in open('/proc/mounts'):
        /usr/lib/python2.6/dist-packages/apport/packaging_impl.py:128:for line in open(f):
        /usr/lib/python2.6/dist-packages/apport/packaging_impl.py:190:for line in open(sumfile):
        /usr/lib/python2.6/dist-packages/apport/packaging_impl.py:641:for l in open('/etc/apt/sources.list'):
        /usr/lib/python2.6/dist-packages/apport/hookutils.py:190:for line in open('/proc/asound/cards'):
        /usr/lib/python2.6/dist-packages/apport/hookutils.py:290:for line in open('/var/log/syslog'):
        /usr/lib/python2.6/dist-packages/apport/hookutils.py:493:mods = [l.split()[0] for l in open(module_list)]
        /usr/lib/python2.6/dist-packages/softwareproperties/SoftwareProperties.py:597:for line in open(f):
        /usr/lib/python2.6/dist-packages/softwareproperties/gtk/SoftwarePropertiesGtk.py:883:for x in open(tmp.name):
        /usr/lib/python2.6/dist-packages/lsb_release.py:253:for line in open('/etc/lsb-release'):
        /usr/lib/python2.6/dist-packages/numpy/distutils/system_info.py:815:for d in open(ld_so_conf,'r').readlines():
        /usr/lib/python2.6/dist-packages/LanguageSelector/LocaleInfo.py:72:for line in open(languagelist_file):
        /usr/lib/python2.6/dist-packages/LanguageSelector/LocaleInfo.py:187:for line in open(environment).readlines():
        /usr/lib/python2.6/dist-packages/LanguageSelector/LocaleInfo.py:193:for line in open(environment).readlines():
        /usr/lib/python2.6/dist-packages/LanguageSelector/LanguageSelector.py:125:for line in open(fname):
        /usr/lib/python2.6/dist-packages/LanguageSelector/LanguageSelector.py:140:for line in open(fname):
        /usr/lib/python2.6/dist-packages/LanguageSelector/LanguageSelector.py:171:for line in open(fname):
        /usr/lib/python2.6/dist-packages/LanguageSelector/LanguageSelector.py:210:for line in open(fname):
        /usr/lib/python2.6/dist-packages/LanguageSelector/macros.py:16:for l in open(file):
        /usr/lib/python2.6/dist-packages/LanguageSelector/macros.py:37:for l in open(self.LANGCODE_TO_LOCALE):
        /usr/lib/python2.6/dist-packages/LanguageSelector/LangCache.py:94:for l in open(self.BLACKLIST):
        /usr/lib/python2.6/dist-packages/LanguageSelector/LangCache.py:99:for l in open(self.LANGCODE_TO_LOCALE):
        /usr/lib/python2.6/dist-packages/LanguageSelector/LangCache.py:111:for l in open(self.PACKAGE_DEPENDS):
        /usr/lib/python2.6/dist-packages/LanguageSelector/ImSwitch.py:78:for l in open(self.blacklist_file):
        

        【讨论】:

        • 这个分析很有趣,但显然不完整。首先,它没有通过气味测试:如果 Python 标准库中只有 52 个位置可以打开文件,我会做饭吃鞋。举个简单的例子,logging 模块实现了自己的 open 和 close 方法,您的 RE 找不到这些方法。另外:它实现了close 方法。这是有原因的。
        • CPython 使用 refcounting 并将确定性地关闭这些文件。 CPython 附带的库能够依赖于 CPython 特定的行为是有道理的。
        • 同样,您没有在测试中看到泄漏的资源,因为您没有保留引用对象,或其他方法来保留对现在不需要但打开的文件的引用---并且 CPython 当前的引用计数会为您确定性地关闭它们。
        • @Robert Rossney:感谢您的评论。以上不是所有文件打开位置的列表。这只是我的 grep 找到 for ... in open(...) 成语的地方的列表。我不遵循您所说的有关日志记录模块的内容。它不使用短成语,但也不使用长成语。您能否进一步解释一下日志记录的相关性?
        • @Roger Pate:也谢谢你。确实,我所了解的仅适用于 CPython。我同意,如果你对回溯做了一些不寻常的事情,它可能会阻止文件描述符关闭。你能展示一些关于这可能发生的代码吗?我认为这将有助于查看演示短成语问题的代码。用户是否可以手动回溯到del文件对象?这可能是一个更好的解决方案,并且必须在任何地方使用长成语?
        【解决方案6】:

        到处都有暗示,但为了最清楚,是的,您需要关闭该文件。在 Python 2.5(使用 future)和 Python 2.6 中,您不再需要冗长的版本:

        from __future__ import with_statement
        with open("hello.txt") as f:
            for line in f:
                print line
        

        【讨论】:

          【解决方案7】:

          退出时,Python 解释器(或崩溃时的内核)将关闭文件,但在不需要它们时关闭它们仍然是一个好习惯。对于 1 个或 2 个或 10 个文件,这可能不是问题,但更多可能会导致整个系统崩溃。

          最重要的是,这表明编写代码的人实际上关心他的工作。

          【讨论】:

          • 实际上,您甚至不必等待进程停止。垃圾收集时将关闭。有关详细信息,请参阅我的答案。
          【解决方案8】:

          您需要关闭句柄以释放内存。在一次处理大量文件之前并不真正需要。

          【讨论】:

          • 通常仍然认为关闭文件是一种好习惯。我想打开一两个文件并不会造成任何损害,但关闭它并不是那么多工作。
          猜你喜欢
          • 2012-08-03
          • 2013-03-27
          • 1970-01-01
          • 1970-01-01
          • 2019-01-22
          • 1970-01-01
          • 2021-12-19
          • 2020-08-23
          • 2021-10-27
          相关资源
          最近更新 更多