web-dev-qa-db-fra.com

Est close () nécessaire lors de l'utilisation de l'itérateur sur un objet fichier Python

Est-ce une mauvaise pratique de faire ce qui suit et pas de gérer explicitement un objet fichier et d'appeler sa méthode close()?

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

NB - c'est pour les versions de Python qui n'ont pas encore l'instruction with.

Je demande car la documentation Python semble recommander ceci: -

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

Ce qui semble plus verbeux que nécessaire.

42
Dave M.

Fermer est toujours nécessaire lors du traitement des fichiers, ce n'est pas une bonne idée de laisser les poignées de fichiers ouvertes partout. Ils seront finalement fermés lorsque l'objet fichier sera récupéré, mais vous ne savez pas quand cela se produira et, en attendant, vous gaspillerez les ressources système en maintenant les poignées de fichier dont vous n'avez plus besoin.

Si vous utilisez Python 2.5 et supérieur, la close() peut être appelée automatiquement à l'aide de l'instruction with:

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

Cela a le même effet que le code que vous avez:

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

L'instruction with est un support de langage direct pour l'idiome Resource Acquisition Is Initialization couramment utilisé en C++. Il permet l'utilisation et le nettoyage en toute sécurité de toutes sortes de ressources, par exemple, il peut être utilisé pour toujours s'assurer que les connexions à la base de données sont fermées ou que les verrous sont toujours libérés comme ci-dessous.

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

En fait, le fichier sera fermé quand il sera ordures récupérées . Voir cette question pour en savoir plus sur comment cela fonctionne.

Il est toujours recommandé d'utiliser un bloc try/finally ou une instruction with. S'il existe une exception lors de l'utilisation de l'une des méthodes de l'objet fichier, une référence sera stockée dans le traceback (qui est stockée en tant que variable globale) jusqu'à ce que vous l'effaciez ou qu'une autre exception se produise.

Ainsi, il est mauvais de s'appuyer sur la récupération de place pour fermer votre fichier pour vous.

De plus, si vous avez écrit dans le fichier, vous ne pouvez pas garantir que les modifications seront enregistrées dans le fichier jusqu'à ce qu'il soit fermé ou vidé.

19
Jason Baker

Étrange que pour toute la discussion dans ce sujet de l'importance de libérer des ressources système, personne n'ait mentionné ce qui me semble une raison évidemment plus importante pour fermer un fichier de façon déterministe: pour qu'il puisse être rouvert.

Il y a certainement des cas où cela n'a pas d'importance. Si un objet fichier sort de la portée ou est supprimé, le fichier sous-jacent sera fermé. (Quand il sera fermé dépend de l'implémentation spécifique de Python que vous utilisez.) Cela va généralement être assez bon - si vous savez exactement quand la variable de fichier va sortir de la portée, et si vous savez que vous ne vous souciez pas de la fermeture déterministe du fichier.

Mais pourquoi devriez-vous même vous préoccuper de ce type d'analyse lorsque l'instruction with existe?

11
Robert Rossney

C'est un peu laissé entendre partout, mais pour que ce soit le plus clair, oui, vous devez fermer ce fichier. Dans Python 2.5 (en utilisant future) et dans Python 2.6, vous n'avez plus besoin de la version verbeuse:

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

A la sortie, l'interpréteur Python (ou le noyau en cas de plantage) fermera le fichier, mais c'est toujours une bonne pratique de le fermer lorsque vous n'en avez pas besoin. Pour 1 ou 2 ou 10 fichiers, ce n'est peut-être pas un problème, mais pour plus, cela peut faire tomber tout le système.

Plus important encore, c'est un signe que la personne qui a écrit le code se soucie de son travail.

5
Emil Ivanov

Non, je ne crois pas que l'idiome plus long soit nécessaire, et voici pourquoi:

J'ai récupéré /usr/lib/python2.6/ pour le motif 'for\s+.*\s+in\s+open\(' et a trouvé de nombreux exemples de

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

et jusqu'à présent zéro cas de

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

Voir ci-dessous pour la liste des fichiers de la bibliothèque standard qui utilisent le for ... in open idiome.

Cela conduit naturellement à la question: si les développeurs Python acceptent l'idiome le plus court dans les bibliothèques standard, comment pouvons-nous améliorer quoi que ce soit en utilisant quelque chose de différent dans notre propre code si notre code dépend de bibliothèques standard ?

Je pense que la réponse est que l'idiome plus long n'améliore rien.

J'ai aussi couru

#!/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()

et vérifié /proc/PID/fd pour la fermeture du descripteur de fichier. Il semble que lorsque vous sortez de la boucle for, le fichier est fermé pour vous.

Sur la base de ces expériences, je ne crois pas que le long try...finally...close l'idiome est nécessaire.

Voici le résultat de la 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

Oui, sinon vous risquez de perdre des ressources.

Depuis les documents Python :

Lorsque vous avez terminé avec un fichier, appelez f.close() pour le fermer et libérer toutes les ressources système occupées par le fichier ouvert.

Cela se produira pour vous à la fin du programme, mais sinon Python conserve les ressources dont il n'a plus besoin jusqu'à ce point.

3
Dominic Rodger

Vous devez fermer les poignées pour libérer de la mémoire. Pas vraiment nécessaire avant de traiter un grand nombre de fichiers à la fois.

2
Sarfraz