web-dev-qa-db-ja.com

Pythonファイルオブジェクトでイテレータを使用する場合はclose()が必要です

以下を実行するのは悪い習慣ですか?notファイルオブジェクトを明示的に処理し、その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()

これは必要以上に冗長に思えます。

42
Dave M.

ファイルを処理するときは、閉じるalwaysが必要です。ファイルハンドルを開いたままにしておくことはお勧めできません。ファイルオブジェクトがガベージコレクションされると、最終的に閉じられますが、いつになるかはわかりませんが、その間、不要になったファイルハンドルを保持してシステムリソースを浪費することになります。

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
66
Tendayi Mawushe

実際には、ファイルは garbage collected のときに閉じられます。その仕組みの詳細については、 この質問 を参照してください。

ただし、try/finallyブロックまたはwithステートメントを使用することをお勧めします。ファイルオブジェクトのメソッドの1つを使用しているときに例外がある場合、参照がクリアされるか別の例外が発生するまで、参照はトレースバック(グローバル変数として格納されます)に格納されます。

したがって、ファイルを閉じるためにガベージコレクションに依存するのは悪いことです。

また、ファイルに書き込んだ場合、ファイルが閉じられるかフラッシュされるまで、変更がファイルに保存されることを保証できません。

19
Jason Baker

システムリソースを解放することの重要性に関するこのトピックのすべての議論について、決定的にファイルを閉じる明確で重要な理由と思われることについて誰も言及していません。

それが重要でない場合は確かにあります。ファイルオブジェクトが範囲外になるか削除されると、基になるファイルは閉じられます。 (いつ閉じられるかは、Pythonを使用している特定の実装に依存します。)それは一般的には十分でしょう-ifファイル変数が正確にいつ範囲外になりますifファイルが確定的に閉じられるかどうかは気にしません。

しかし、なぜwithステートメントが存在するのに、この種の分析に悩まされる必要があるのでしょうか?

11
Robert Rossney

いたるところにヒントがありますが、最も明確にするためには、そのファイルを閉じる必要があります。 Python 2.5(futureを使用)およびin Python 2.6では、冗長なバージョンは不要になりました。

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

終了時にPythonインタープリター(またはクラッシュの場合はカーネル)がファイルを閉じますが、必要のない場合は閉じることをお勧めします。1または2、または10のファイルは問題ではないかもしれませんが、それ以上の場合はシステム全体がダウンする可能性があります。

最も重要なことは、コードを実際に書いた人が自分の仕事についてcaresであるというサインです。

5
Emil Ivanov

いいえ、長いイディオムが必要だとは思いませんが、その理由は次のとおりです。

パターン'for\s+.*\s+in\s+open\('の/usr/lib/python2.6/をgrepし、多くの例を見つけました

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):
3
unutbu

はい、そうしないとリソースがリークする可能性があります。

Pythonドキュメント から:

ファイルの処理が完了したら、f.close()を呼び出してファイルを閉じ、開いているファイルによって占有されているシステムリソースを解放します。

これは、プログラムが終了するときに発生しますが、それ以外の場合はPythonはその時点まで必要のないリソースを保持しています。

3
Dominic Rodger

メモリが解放されるように、ハンドルを閉じる必要があります。一度に多くのファイルを処理するまで、本当に必要ありません。

2
Sarfraz