在Python文件对象上使用迭代器时,是否需要close()

Dav*_* M. 42 python iterator file

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

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

注意 - 这是针对尚未拥有该with语句的Python版本.

我问,因为Python文档似乎推荐这个: -

f = open("hello.txt")
try:
    for line in f:
        print line
finally:
    f.close()
Run Code Online (Sandbox Code Playgroud)

这似乎比必要的更冗长.

Ten*_*she 66

在处理文件时始终需要关闭,在整个地方保留打开的文件句柄并不是一个好主意.当文件对象被垃圾收集时,它们最终会被关闭,但是你不知道什么时候会被收集,同时你将通过持有你不再需要的文件句柄来浪费系统资源.

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

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

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

f = open("hello.txt")
try:
    for line in f:
        print line
finally:
    f.close()
Run Code Online (Sandbox Code Playgroud)

该with语句是对C++中常用的资源获取是初始化习语的直接语言支持.它允许安全使用和清理各种资源,例如,它可以用于始终确保数据库连接已关闭或锁定始终如下所示.

mylock = threading.Lock()
with mylock:
    pass # do some thread safe stuff
Run Code Online (Sandbox Code Playgroud)

  • `with`语句很简单,为了清楚起见,很难证明不使用它. (10认同)
  • 在小型,短期运行的脚本中,谨慎的做法是将代码的清晰度与一个打开的文件暂时挂起arodund进行权衡. (2认同)

Jas*_*ker 19

实际上,文件将在垃圾收集时关闭.有关其工作原理的更多信息,请参阅此问题.

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

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

此外,如果您已写入文件,则无法保证更改将保存到文件中,直到它被关闭或刷新为止.

  • +1其余的答案只是引用文档,你给出了使用try/finally的一个很好的解释. (2认同)

Rob*_*ney 12

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

肯定存在无关紧要的情况.如果文件对象超出范围或被删除,则基础文件将关闭.(当它被关闭,取决于具体的实现你使用Python的.)这是通常会是不够好- 如果你知道什么时候该文件的变量是要出去的范围,如果你知道你不"关心文件是否确定性地关闭.

但是,当with声明存在时,为什么你甚至要用这种分析来困扰自己呢?


Dou*_*yle 6

它在所有地方都有所暗示,但为了使其最清晰,是的,你需要关闭该文件.在Python 2.5(使用未来)和Python 2.6中,您不再需要罗嗦版本:

from __future__ import with_statement
with open("hello.txt") as f:
    for line in f:
        print line
Run Code Online (Sandbox Code Playgroud)


unu*_*tbu 6

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

我在 /usr/lib/python2.6/ 中查找该模式'for\s+.*\s+in\s+open\(' 并找到了许多示例

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

到目前为止零实例

f = open("hello.txt")
try:
    for line in f:
        print line
finally:
    f.close()
Run Code Online (Sandbox Code Playgroud)

请参阅下面的标准库中使用该习惯用法的文件列表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()
Run Code Online (Sandbox Code Playgroud)

并检查/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):
Run Code Online (Sandbox Code Playgroud)

  • 这个分析很有趣,但显然不完整。首先,它没有通过气味测试:如果Python标准库中只有52个位置打开了文件,我就会煮并吃掉我的鞋子。举一个简单的例子,“logging”模块实现了自己的“open”和“close”方法,您的 RE 找不到这些方法。另外:它实现了一个“close”方法。这是有原因的。 (2认同)

Emi*_*nov 5

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

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

  • 实际上,您甚至不必等待进程停止。收集垃圾时将关闭。请参阅我的答案以获取更多详细信息。 (2认同)

Sar*_*raz 2

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