120 votes

Vérifier si l'objet est de type fichier en Python

Objets de type fichier sont des objets en Python qui se comportent comme un véritable fichier, c'est-à-dire qu'ils possèdent une méthode de lecture () et une méthode d'écriture (), mais dont l'implémentation est différente de celle de l'outil de gestion des fichiers. file . C'est la réalisation de la Dactylographie du canard concept.

Il est considéré comme une bonne pratique d'autoriser un objet de type fichier partout où l'on s'attend à ce qu'il y ait un fichier. StringIO ou un objet Socket peut être utilisé à la place d'un fichier réel. Il n'est donc pas bon d'effectuer une telle vérification :

if not isinstance(fp, file):
   raise something

Quelle est la meilleure façon de vérifier si un objet (par exemple, un paramètre d'une méthode) est "de type fichier" ?

131voto

abarnert Points 94246

Pour 3.1+, l'un des éléments suivants :

isinstance(something, io.TextIOBase)
isinstance(something, io.BufferedIOBase)
isinstance(something, io.RawIOBase)
isinstance(something, io.IOBase)

Pour la version 2.x, l'expression "objet de type fichier" est trop vague pour être vérifiée, mais la documentation de la ou des fonctions auxquelles vous avez affaire devrait vous indiquer ce dont elles ont besoin ; sinon, lisez le code.


Comme le soulignent d'autres réponses, la première chose à demander est de savoir ce que l'on cherche exactement. Habituellement, EAFP est suffisant, et plus idiomatique.

Le glossaire indique que "file-like object" est un synonyme de "file object", ce qui signifie en fin de compte qu'il s'agit d'une instance de l'un des trois types d'objets suivants classes de base abstraites défini dans le site io module qui sont elles-mêmes des sous-classes de IOBase . Donc, la façon de vérifier est exactement comme indiqué ci-dessus.

(Toutefois, la vérification IOBase n'est pas très utile. Pouvez-vous imaginer un cas où vous auriez besoin de distinguer un fichier réel, par exemple read(size) à partir d'une fonction à un argument nommée read qui ne ressemble pas à un fichier, sans avoir à faire la distinction entre les fichiers texte et les fichiers binaires bruts ? Donc, en réalité, vous voulez presque toujours vérifier, par exemple, "est un objet de type fichier texte", et non "est un objet de type fichier").


Pour la version 2.x, alors que le io existe depuis la version 2.6+, les objets fichiers intégrés ne sont pas des instances du module io ni aucun des objets de type fichier de la stdlib, ni la plupart des objets de type fichier tiers que vous êtes susceptible de rencontrer. Il n'y a pas eu de définition officielle de ce que signifie "objet de type fichier" ; c'est simplement "quelque chose comme un objet intégré". Objet du fichier "et différentes fonctions ont des significations différentes de "like". Ces fonctions devraient documenter ce qu'elles signifient ; si elles ne le font pas, vous devez regarder le code.

Cependant, les significations les plus courantes sont "a read(size) ", "a read() ", ou "est un itérable de chaînes de caractères", mais certaines anciennes bibliothèques peuvent s'attendre à ce que readline au lieu de l'un d'entre eux, certaines bibliothèques aiment à close() fichiers que vous leur donnez, certains s'attendront à ce que si fileno est présent, alors une autre fonctionnalité est disponible, etc. Et de même pour write(buf) (bien qu'il y ait beaucoup moins d'options dans cette direction).

3 votes

Enfin, quelqu'un qui garde les pieds sur terre.

44 votes

La seule réponse utile. La raison pour laquelle les StackOverflowers continuent d'upvoter les posts "Arrête de faire ce que tu essaies de faire, parce que je sais mieux que toi... et PEP 8, EAFP, et d'autres trucs !" dépasse mon équilibre mental fragile. ( Peut-être que Cthulhu le sait ? )

3 votes

Parce que nous avons rencontré beaucoup trop de code écrit par des gens qui n'ont pas réfléchi à l'avance, et il se casse quand vous lui passez quelque chose qui est presque, mais pas tout à fait un fichier parce qu'ils vérifient explicitement. L'ensemble de l'EAFP, le typage des canards n'est pas une connerie de test de pureté. C'est une véritable décision d'ingénierie,

46voto

Scott Griffiths Points 8867

Comme d'autres l'ont dit, vous devriez généralement éviter de tels contrôles. Une exception est lorsque l'objet peut légitimement être de différents types et que vous voulez un comportement différent selon le type. La méthode EAFP ne fonctionne pas toujours dans ce cas, car un objet peut ressembler à plus d'un type de canard !

Par exemple, un initialisateur peut prendre un fichier, une chaîne ou une instance de sa propre classe. Vous pourriez alors avoir du code comme :

class A(object):
    def __init__(self, f):
        if isinstance(f, A):
            # Just make a copy.
        elif isinstance(f, file):
            # initialise from the file
        else:
            # treat f as a string

L'utilisation de l'EAFP ici pourrait causer toutes sortes de problèmes subtils car chaque chemin d'initialisation est partiellement exécuté avant de lancer une exception. Essentiellement, cette construction imite la surcharge de fonctions et n'est donc pas très pythique, mais elle peut être utile si elle est utilisée avec précaution.

À titre d'information, vous ne pouvez pas effectuer la vérification des fichiers de la même manière dans Python 3. Vous aurez besoin de quelque chose comme isinstance(f, io.IOBase) à la place.

41voto

Tendayi Mawushe Points 10335

En général, il n'est pas bon d'avoir des contrôles de ce type dans votre code, à moins que vous n'ayez des exigences particulières.

En Python, le typage est dynamique, pourquoi ressentez-vous le besoin de vérifier si l'objet ressemble à un fichier, plutôt que de l'utiliser simplement comme si c'était un fichier et de gérer l'erreur qui en résulte ?

Toutes les vérifications que vous pouvez faire se feront de toute façon au moment de l'exécution, donc faire quelque chose comme if not hasattr(fp, 'read') et lever une exception n'apporte guère plus d'utilité que de simplement appeler fp.read() et traiter l'erreur d'attribut qui en résulte si la méthode n'existe pas.

1 votes

why qu'en est-il des opérateurs comme __add__ , __lshift__ o __or__ dans les classes personnalisées ? (fichier objet et API : docs.python.org/glossary.html#terme-file-objet )

0 votes

@naxa:Qu'en est-il exactement de ces opérateurs ?

43 votes

Souvent, le simple fait d'essayer fonctionne, mais je ne crois pas à la maxime pythonienne selon laquelle si c'est difficile à faire en Python, c'est que c'est faux. Imaginez qu'on vous passe un objet et qu'il y a 10 choses différentes que vous pouvez faire avec cet objet en fonction de son type. Vous n'allez pas essayer chaque possibilité et gérer l'erreur jusqu'à ce que vous réussissiez. Ce serait totalement inefficace. Vous n'avez pas nécessairement besoin de demander quel est le type de cet objet, mais vous devez être capable de demander si cet objet implémente l'interface X.

27voto

drxzcl Points 2156

Le paradigme dominant ici est EAFP : plus facile de demander pardon que la permission. Allez-y et utilisez l'interface de fichier, puis gérez les exceptions qui en résultent, ou laissez-les se propager à l'appelant.

9 votes

+1 : Si x n'est pas de type fichier, alors x.read() lèvera sa propre exception. Pourquoi écrire une instruction if supplémentaire ? Utilisez simplement l'objet. Soit il fonctionnera, soit il se cassera.

3 votes

Ne vous occupez même pas de l'exception. Si quelqu'un a transmis quelque chose qui ne correspond pas à l'API que vous attendez, ce n'est pas votre problème.

1 votes

@Aaron Gallagher : Je ne suis pas sûr. Votre affirmation est-elle vraie même s'il est difficile pour moi de préserver un état cohérent ?

11voto

Ben DeMott Points 852

Il est souvent utile de soulever une erreur en vérifiant une condition, alors que cette erreur ne serait normalement soulevée que beaucoup plus tard. Cela est particulièrement vrai pour la frontière entre le code "user-land" et le code "api".

Dans un poste de police, on ne placerait pas un détecteur de métaux sur la porte de sortie, mais à l'entrée ! Si le fait de ne pas vérifier une condition signifie qu'une erreur peut se produire qui aurait pu être détectée 100 lignes plus tôt, ou dans une super-classe au lieu d'être soulevée dans la sous-classe, alors je dis qu'il n'y a rien de mal à vérifier.

La vérification des types appropriés est également utile lorsque vous acceptez plus d'un type. Il est préférable de lever une exception qui dit "J'ai besoin d'une sous-classe de basestring, OU de file" plutôt que de lever une exception parce qu'une variable n'a pas de méthode 'seek'...

Cela ne veut pas dire que vous devez devenir fou et faire cela partout, pour la plupart, je suis d'accord avec le concept des exceptions qui se lèvent d'elles-mêmes, mais si vous pouvez rendre votre API drastiquement claire, ou éviter l'exécution de code inutile parce qu'une simple condition n'a pas été remplie, faites-le !

1 votes

Je suis d'accord, mais dans l'optique de ne pas faire de folies partout - beaucoup de ces préoccupations devraient se dissiper au cours des tests, et certaines des questions "où attraper ceci/comment afficher à l'utilisateur" trouveront leur réponse dans les exigences de convivialité.

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