Il y a exactement une raison pour laquelle ce qui suit est préféré:
with open('filename.txt') as fp:
for line in fp:
print line
Nous sommes tous gâtés par le schéma de comptage de références relativement déterministe de CPython pour le garbage collection. D'autres implémentations hypothétiques de Python ne fermeront pas nécessairement le fichier "assez rapidement" sans le with
bloc si elles utilisent un autre schéma pour récupérer la mémoire.
Dans une telle implémentation, vous pouvez obtenir une erreur "trop de fichiers ouverts" du système d'exploitation si votre code ouvre les fichiers plus rapidement que le garbage collector appelle les finaliseurs sur les descripteurs de fichiers orphelins. La solution habituelle de contournement consiste à déclencher le GC immédiatement, mais c'est un mauvais hack et cela doit être fait par chaque fonction qui pourrait rencontrer l'erreur, y compris celles des bibliothèques. Quel cauchemard.
Ou vous pouvez simplement utiliser le with
bloc.
Question bonus
(Arrêtez de lire maintenant si vous n'êtes intéressé que par les aspects objectifs de la question.)
Pourquoi cela n'est-il pas inclus dans le protocole d'itérateur pour les objets fichier?
C'est une question subjective sur la conception d'API, j'ai donc une réponse subjective en deux parties.
Au niveau de l'intestin, cela semble faux, car cela oblige le protocole de l'itérateur à faire deux choses distinctes - itérer sur les lignes et fermer le descripteur de fichier - et c'est souvent une mauvaise idée de faire faire deux actions à une fonction simple. Dans ce cas, cela se sent particulièrement mal car les itérateurs sont liés de manière quasi fonctionnelle et basée sur la valeur au contenu d'un fichier, mais la gestion des descripteurs de fichiers est une tâche complètement distincte. Ecraser les deux, de manière invisible, en une seule action est surprenant pour les humains qui lisent le code et rend plus difficile le raisonnement sur le comportement du programme.
D'autres langues sont essentiellement parvenues à la même conclusion. Haskell a brièvement flirté avec le soi-disant «paresseux IO» qui vous permet d'itérer sur un fichier et de le fermer automatiquement lorsque vous arrivez à la fin du flux, mais il est presque universellement découragé d'utiliser des IO paresseux dans Haskell ces jours-ci, et Haskell les utilisateurs sont généralement passés à une gestion des ressources plus explicite comme Conduit qui se comporte plus comme le with
bloc en Python.
Sur le plan technique, il y a certaines choses que vous voudrez peut-être faire avec un descripteur de fichier en Python qui ne fonctionneraient pas aussi bien si l'itération fermait le descripteur de fichier. Par exemple, supposons que je doive parcourir le fichier deux fois:
with open('filename.txt') as fp:
for line in fp:
...
fp.seek(0)
for line in fp:
...
Bien que ce soit un cas d'utilisation moins courant, considérez le fait que j'aurais pu simplement ajouter les trois lignes de code en bas à une base de code existante qui avait à l'origine les trois lignes supérieures. Si l'itération fermait le fichier, je ne pourrais pas le faire. Ainsi, séparer les itérations et la gestion des ressources permet de composer plus facilement des morceaux de code dans un programme Python plus volumineux et fonctionnel.
La composabilité est l'une des fonctionnalités d'utilisabilité les plus importantes d'un langage ou d'une API.