Python - assert vs if & return

12

J'écris un script qui fait quelque chose dans un fichier texte (ce qu'il fait n'est cependant pas pertinent pour ma question). Donc, avant de faire quelque chose au fichier, je veux vérifier si le fichier existe. Je peux le faire, pas de problème, mais le problème est plutôt celui de l'esthétique.

Voici mon code, implémentant la même chose de deux manières différentes.

def modify_file(filename):
    assert os.path.isfile(filename), 'file does NOT exist.'


Traceback (most recent call last):
  File "clean_files.py", line 15, in <module>
    print(clean_file('tes3t.txt'))
  File "clean_files.py", line 8, in clean_file
    assert os.path.isfile(filename), 'file does NOT exist.'
AssertionError: file does NOT exist.

ou:

def modify_file(filename):
    if not os.path.isfile(filename):
        return 'file does NOT exist.'


file does NOT exist.

La première méthode produit une sortie qui est principalement triviale, la seule chose qui m'importe, c'est que le fichier n'existe pas.

La deuxième méthode renvoie une chaîne, c'est simple.

Ma question est: quelle méthode est la meilleure pour faire savoir à l'utilisateur que le fichier n'existe pas? L'utilisation de la assertméthode semble en quelque sorte plus pythonique.

Vader
la source

Réponses:

33

Vous opteriez plutôt pour une troisième option: utiliser raiseet une exception spécifique. Il peut s'agir de l'une des exceptions intégrées ou vous pouvez créer une exception personnalisée pour le travail.

Dans ce cas, j'utiliserais IOError, mais un ValueErrorpourrait également convenir:

def modify_file(filename):
    if not os.path.isfile(filename):
        raise IOError('file does NOT exist.')

L'utilisation d'une exception spécifique vous permet de lever d' autres exceptions pour différentes circonstances exceptionnelles et permet à l'appelant de gérer l'exception avec élégance.

Bien sûr, de nombreuses opérations de fichiers (comme open()) elles-mêmes se déclenchent OSErrordéjà; tester explicitement si le fichier existe peut être redondant ici.

Ne pas utiliser assert; si vous exécutez python avec l' -Oindicateur, toutes les assertions sont supprimées du code.

Martijn Pieters
la source
point intéressant sur la redondance! Recommanderiez-vous de l'éviter? c'est-à-dire "il échouera de toute façon plus tard"
Ciprian Tomoiagă
1
@ CiprianTomoiagă: pourquoi doubler les tests? Si la prochaine ligne de modify_file()est with open(filename) as f:, alors IOErrorserait également augmentée. Et les versions plus récentes de Python ont fourni plus de détails dans les sous-classes de IOError(cela FileNotFoundErrorme vient spécifiquement à l'esprit) qui pourraient être utiles pour un développeur utilisant cette API. Si le code effectue ses propres vérifications et augmente IOErroralors ces détails utiles seraient perdus.
Martijn Pieters
@MartijnPieters serait une réponse acceptable à "pourquoi doubler les tests?" être quand la première vérification est plus rapide que l'exception levée lorsque open () échoue? Par exemple, il est plus rapide de vérifier l'existence d'un fichier que d'essayer de l'ouvrir et de ne pas le faire.
Marcel Wilson
1
@MarcelWilson: Non, car vous seriez en train de faire une micro-optimisation par rapport à une méthode qui fait des E / S. Aucune modification de la sémantique Python minute n'accélérera les E / S et ne nuira qu'à la lisibilité et à la maintenabilité. Concentrez-vous sur les domaines ayant plus d'impact, je dirais.
Martijn Pieters
12

assertest destiné aux cas où le programmeur appelant la fonction a fait une erreur, par opposition à l' utilisateur . L'utilisation assertdans ces circonstances vous permet de vous assurer qu'un programmeur utilise correctement votre fonction pendant les tests, puis de la supprimer en production.

Sa valeur est quelque peu limitée car vous devez vous assurer d'exercer ce chemin dans le code et vous souhaitez souvent gérer le problème en plus avec une ifinstruction distincte en production. assertest le plus utile dans des situations telles que «Je veux contourner ce problème si un utilisateur le rencontre, mais si un développeur le rencontre, je veux qu'il plante brutalement afin qu'il corrige le code qui appelle cette fonction de manière incorrecte».

Dans votre cas particulier, un fichier manquant est presque certainement une erreur utilisateur et doit être traité en levant une exception.

Karl Bielefeldt
la source
5

De UsingAssertionsEffectively

La vérification de isinstance () ne doit pas être surutilisée: si elle craque comme un canard, il n'est peut-être pas nécessaire de chercher trop profondément si elle l'est vraiment. Parfois, il peut être utile de transmettre des valeurs qui n'étaient pas prévues par le programmeur d'origine.

Lieux à considérer pour mettre des assertions:

checking parameter types, classes, or values
checking data structure invariants
checking "can't happen" situations (duplicates in a list, contradictory state variables.)
after calling a function, to make sure that its return is reasonable 

Le point global est que si quelque chose ne va pas, nous voulons que cela soit complètement évident dès que possible.

Il est plus facile d'attraper des données incorrectes au moment où elles entrent que de déterminer comment elles y sont arrivées plus tard lorsqu'elles causent des problèmes.

Les assertions ne remplacent pas les tests unitaires ou les tests système, mais plutôt un complément. Parce que les assertions sont un moyen propre d'examiner l'état interne d'un objet ou d'une fonction, elles fournissent "gratuitement" une assistance en clair à un test de boîte noire qui examine le comportement externe.

Les assertions ne doivent pas être utilisées pour tester les cas d'échec qui peuvent se produire en raison d'une entrée utilisateur incorrecte ou de défaillances du système d'exploitation / de l'environnement, telles qu'un fichier introuvable. Au lieu de cela, vous devez déclencher une exception, ou imprimer un message d'erreur, ou tout ce qui est approprié. Une raison importante pour laquelle les assertions ne doivent être utilisées que pour les auto-tests du programme est que les assertions peuvent être désactivées au moment de la compilation.

Si Python est démarré avec l'option -O, les assertions seront supprimées et non évaluées. Donc, si le code utilise fortement les assertions, mais est essentiel aux performances, il existe un système pour les désactiver dans les versions. (Mais ne faites pas cela à moins que ce ne soit vraiment nécessaire. Il a été scientifiquement prouvé que certains bogues n'apparaissent que lorsqu'un client utilise la machine et nous voulons que les assertions y aident aussi. :-))

dspjm
la source