La lecture d'un fichier entier laisse-t-elle le descripteur de fichier ouvert?


372

Si vous lisez un fichier entier avec, content = open('Path/to/file', 'r').read()le descripteur de fichier est-il laissé ouvert jusqu'à la fin du script? Existe-t-il une méthode plus concise pour lire un fichier entier?

Réponses:


585

La réponse à cette question dépend quelque peu de l'implémentation particulière de Python.

Pour comprendre de quoi il s'agit, portez une attention particulière à l' fileobjet réel . Dans votre code, cet objet n'est mentionné qu'une seule fois, dans une expression, et devient inaccessible immédiatement après le read()retour de l' appel.

Cela signifie que l'objet fichier est des ordures. La seule question qui reste est "Quand le garbage collector va-t-il récupérer l'objet fichier?".

dans CPython, qui utilise un compteur de référence, ce type de déchets est immédiatement remarqué et sera donc immédiatement collecté. Ce n'est généralement pas vrai pour les autres implémentations de python.

Une meilleure solution, pour vous assurer que le fichier est fermé, est ce modèle:

with open('Path/to/file', 'r') as content_file:
    content = content_file.read()

qui fermera toujours le fichier immédiatement après la fin du bloc; même si une exception se produit.

Edit: Pour mettre un point plus fin dessus:

Autre que file.__exit__(), qui est appelé "automatiquement" dans un withparamètre de gestionnaire de contexte, la seule autre manière qui file.close()est automatiquement appelée (c'est-à-dire autre que de l'appeler explicitement vous-même) est via file.__del__(). Cela nous amène à la question de savoir quand est __del__()appelé?

Un programme correctement écrit ne peut pas supposer que les finaliseurs s'exécuteront à un moment quelconque avant la fin du programme.

- https://devblogs.microsoft.com/oldnewthing/20100809-00/?p=13203

En particulier:

Les objets ne sont jamais explicitement détruits; cependant, lorsqu'ils deviennent inaccessibles, ils peuvent être récupérés. Une implémentation est autorisée à reporter le garbage collection ou à l'omettre complètement - c'est une question de qualité d'implémentation comment le garbage collection est implémenté, tant qu'aucun objet collecté n'est encore accessible.

[...]

CPython utilise actuellement un schéma de comptage de références avec (facultatif) détection retardée des ordures liées cycliquement, qui collecte la plupart des objets dès qu'ils deviennent inaccessibles, mais il n'est pas garanti de collecter les ordures contenant des références circulaires.

- https://docs.python.org/3.5/reference/datamodel.html#objects-values-and-types

(Souligner le mien)

mais comme il le suggère, d'autres implémentations peuvent avoir un autre comportement. Par exemple, PyPy a 6 implémentations différentes de la récupération de place !


24
Pendant un moment, il n'y avait pas vraiment d'autres implémentations Python; mais s'appuyer sur les détails de l'implémentation n'est pas vraiment Pythonic.
Karl Knechtel

Est-il toujours spécifique à l'implémentation, ou était-il déjà standardisé? Ne pas appeler __exit__()dans de tels cas ressemble à un défaut de conception.
rr-

2
@jgmjgm C'est précisément à cause de ces 3 problèmes, le GC étant imprévisible, try/ finallyétant délicat et l'utilité très courante des gestionnaires de nettoyage qui withrésout. La différence entre la «fermeture explicite» et la «gestion avec with» est que le gestionnaire de sortie est appelé même si une exception est levée. Vous pouvez mettre le close()dans une finallyclause, mais ce n'est pas très différent de l'utilisation à la withplace, un peu plus compliqué (3 lignes supplémentaires au lieu de 1), et un peu plus difficile à obtenir juste.
SingleNegationElimination

1
Ce que je ne comprends pas, c'est pourquoi «avec» serait plus fiable car il n'est pas explicite non plus. Est-ce parce que la spécification dit qu'il faut que ça soit toujours implémenté comme ça?
jgmjgm

3
@jgmjgm il est plus fiable parce with foo() as f: [...]est fondamentalement la même que f = foo(), f.__enter__()[...] et f.__exit__() avec des exceptions traitées , de sorte que __exit__toujours appelé. Le dossier est donc toujours fermé.
neingeist

104

Vous pouvez utiliser pathlib .

Pour Python 3.5 et supérieur:

from pathlib import Path
contents = Path(file_path).read_text()

Pour les anciennes versions de Python, utilisez pathlib2 :

$ pip install pathlib2

Alors:

from pathlib2 import Path
contents = Path(file_path).read_text()

Ceci est la read_text mise en œuvre réelle :

def read_text(self, encoding=None, errors=None):
    """
    Open the file in text mode, read it, and close the file.
    """
    with self.open(mode='r', encoding=encoding, errors=errors) as f:
        return f.read()

2

Eh bien, si vous devez lire le fichier ligne par ligne pour travailler avec chaque ligne, vous pouvez utiliser

with open('Path/to/file', 'r') as f:
    s = f.readline()
    while s:
        # do whatever you want to
        s = f.readline()

Ou encore mieux:

with open('Path/to/file') as f:
    for line in f:
        # do whatever you want to

0

Au lieu de récupérer le contenu du fichier sous la forme d'une seule chaîne, il peut être pratique de stocker le contenu sous la forme d'une liste de toutes les lignes du fichier :

with open('Path/to/file', 'r') as content_file:
    content_list = content_file.read().strip().split("\n")

Comme on peut le voir, il faut ajouter les méthodes concaténées .strip().split("\n")à la réponse principale de ce fil .

Ici, .strip()supprime simplement les espaces et les caractères de nouvelle ligne à la fin de la chaîne de fichier entière, et .split("\n")produit la liste réelle en divisant la chaîne de fichier entière à chaque caractère de nouvelle ligne \ n .

De plus, de cette façon, tout le contenu du fichier peut être stocké dans une variable, ce qui peut être souhaité dans certains cas, au lieu de parcourir le fichier ligne par ligne comme indiqué dans cette réponse précédente .

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.