144 votes

Comment lire un fichier ligne par ligne en Python ?

À l'époque préhistorique (Python 1.4), c'était le cas :

fp = open('filename.txt')
while 1:
    line = fp.readline()
    if not line:
        break
    print line

après Python 2.1, nous l'avons fait :

for line in open('filename.txt').xreadlines():
    print line

avant d'obtenir le protocole d'itération pratique de Python 2.3, et de pouvoir le faire :

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

J'ai vu quelques exemples utilisant la version la plus verbeuse :

with open('filename.txt') as fp:
    for line in fp:
        print line

Est-ce la méthode préférée pour l'avenir ?

[Je comprends que l'instruction with assure la fermeture du fichier... mais pourquoi cela n'est-il pas inclus dans le protocole d'itération des objets de type fichier ?

241voto

Dietrich Epp Points 72865

Il y a exactement une raison pour laquelle on préfère ce qui suit :

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 la collecte des déchets. D'autres implémentations hypothétiques de Python ne fermeront pas nécessairement le fichier "assez rapidement" sans l'option with s'ils utilisent un autre système pour récupérer la mémoire.

Dans une telle implémentation, vous pouvez obtenir une erreur "trop de fichiers ouverts" de la part du système d'exploitation si votre code ouvre des fichiers plus rapidement que le ramasseur d'ordures appelle les finaliseurs sur les handles de fichiers orphelins. La solution habituelle est de déclencher le GC immédiatement, mais c'est une mauvaise manipulation et cela doit être fait par chaque qui pourraient rencontrer l'erreur, y compris celles des bibliothèques. Quel cauchemar.

Ou vous pouvez simplement utiliser le with bloc.

Question bonus

(Arrêtez de lire maintenant si vous ne vous intéressez qu'aux aspects objectifs de la question).

Pourquoi cela n'est-il pas inclus dans le protocole d'itération des objets fichiers ?

Il s'agit d'une question subjective sur la conception d'une API. J'ai donc une réponse subjective en deux parties.

À un niveau instinctif, cela semble mauvais, parce que le protocole itérateur fait deux choses distinctes - itérer sur les lignes et fermer la poignée du fichier - et c'est souvent une mauvaise idée de faire faire deux actions à une fonction d'apparence simple. Dans ce cas, c'est particulièrement mauvais parce que les itérateurs se rapportent d'une manière quasi-fonctionnelle, basée sur la valeur, au contenu d'un fichier, mais la gestion des handles de fichiers est une tâche complètement séparée. Réduire les deux, de manière invisible, à 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 arrivées à la même conclusion. Haskell a brièvement flirté avec ce que l'on appelle le "lazy 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éconseillé d'utiliser le lazy IO en Haskell de nos jours, et les utilisateurs de Haskell se sont principalement tournés vers une gestion des ressources plus explicite comme Conduit qui se comporte plus comme le with en Python.

D'un point de vue technique, il y a certaines choses que vous pouvez vouloir faire avec un handle de fichier en Python qui ne fonctionneraient pas aussi bien si l'itération fermait le handle de fichier. Par exemple, supposons que je doive itérer deux fois sur le fichier :

with open('filename.txt') as fp:
    for line in fp:
        ...
    fp.seek(0)
    for line in fp:
        ...

Bien qu'il s'agisse d'un cas d'utilisation moins courant, considérez le fait que j'ai peut-être simplement ajouté les trois lignes de code du bas à une base de code existante qui comportait à l'origine les trois lignes du haut. Si l'itération a fermé le fichier, je ne serais pas en mesure de le faire. Ainsi, en gardant l'itération et la gestion des ressources séparées, il est plus facile de composer des morceaux de code dans un programme Python plus grand et fonctionnel.

La composabilité est l'une des caractéristiques de convivialité les plus importantes d'un langage ou d'une API.

22voto

eumiro Points 56644

Oui,

with open('filename.txt') as fp:
    for line in fp:
        print line

est la voie à suivre.

Il n'est pas plus verbeux. C'est plus sûr.

5voto

Lie Ryan Points 24517

Si la ligne supplémentaire vous rebute, vous pouvez utiliser une fonction d'habillage comme suit :

def with_iter(iterable):
    with iterable as iter:
        for item in iter:
            yield item

for line in with_iter(open('...')):
    ...

dans Python 3.3, le yield from rendrait cette déclaration encore plus courte :

def with_iter(iterable):
    with iterable as iter:
        yield from iter

0voto

JiminyCricket Points 1622

Le site Dernière version de la documentation Python pour 2.7 suggère que c'est la voie à suivre, mais bien sûr, c'est toujours vous qui décidez.

D'après l'exemple dans la documentation :

with open('/tmp/workfile', 'r') as f:
    read_data = f.read()

-1voto

Halgurd Points 11
f = open('test.txt','r')
for line in f.xreadlines():
    print line
f.close()

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X