2 votes

Quelle est la manière correcte de fermer un fichier ouvert dans l'initialisation d'un objet ?

J'aimerais avoir une classe à qui l'on transmet soit une chaîne de caractères, soit un fichier déjà ouvert lors de l'initialisation. Si elle reçoit une chaîne, elle ouvre le fichier.

from typing import IO

class Parser:
  def __init__(self, fin: str|IO[str]) -> None:
    if isinstance(fin, str):
      self.fin = open(fin, 'r')
    else:
      if not fin.readable():
        raise ValueError("Input file must be readable.")
      else:
        self.fin = fin

Ma question est la suivante : quelle est la manière correcte de fermer le fichier s'il est ouvert ? J'ai imaginé qu'il pouvait être fermé dans le __del__ mais après avoir lu des articles sur le sujet, il semble que l'on s'accorde à dire que l'utilisation de la méthode __del__ n'est pas une bonne idée. Existe-t-il une meilleure façon de procéder ?

5voto

chepner Points 54078

Fournissez une méthode de classe séparée pour traiter l'ouverture et la fermeture du fichier en utilisant un gestionnaire de contexte.

from contextlib import contextmanager

class Parser:
    def __init__(self, fh: IO[str]) -> None:
        if not fh.readable():
            raise ValueError(...)
        self.fh = fh

    # Typing-hinting is out of scope for this question, so I'm
    # just using the Python-3.11-style Self hint for simplicity.
    @classmethod
    @contextmanager
    def from_filename(cls: Self, name: str) -> Self:
        with open(name) as fh:
            yield Parser(fh)

Maintenant, vous pouvez utiliser soit

with open("some_file.txt") as fh:
    p = Parser(fh)
    ...

ou

with Parser.from_filename("some_file.txt") as p:
    ...

1voto

wovano Points 1581

En effet, __del__ debe PAS ne peut être utilisé à cette fin, car il est hautement imprévisible lorsqu'il est appelé.

De même, le mieux est d'éviter les effets secondaires (comme l'ouverture de fichiers) dans __init__ donc j'aime vraiment le solution par chepner . Lorsque c'est possible, c'est la méthode que je choisirais.

Juste pour ajouter une alternative : vous pourriez aussi implémenter une close et ensuite utiliser la méthode contextlib.closing comme ça :

import contextlib

class Parser:

    # your __init__ here

    def close(self):
        self.fin.close()

parser = Parser('test.txt')
with contextlib.closing(parser):
    # do something with the object here
    # the file will be automatically closed afterwards

Encore mieux, vous pourriez faire de la classe un gestionnaire de contexte lui-même comme ci-dessous, ce qui est un peu similaire à la réponse de chepner. (Vous pourriez même combiner un certain nombre de ces méthodes, en fonction de vos besoins).

class Parser:

    # your __init__ here

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.fin.close()

with Parser('test.txt') as parser:
    # do some stuff here...

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