Un moyen simple d’améliorer la testabilité et la réutilisabilité de votre code
Passez du temps avec les développeurs Python assez longtemps et vous entendrez tout sur Tim Peter Zen de Python.
La Zenque vous pouvez lire commodément en exécutant import this
dans un Python REPL, présente 19 des 20 principes directeurs derrière la conception de Python. Récemment, j’en suis venu à apprécier un aphorisme plus que les autres : « Explicit vaut mieux qu’implicite ».
L’interprétation la plus courante que j’ai vue – si courante qu’elle habite actuellement l’extrait de code de Google lors de la recherche de la phrase – est que le code verbeux est meilleur que le code laconique parce que la verbosité est, apparemment, la clé de la lisibilité… ou quelque chose comme ça.
Bien sûr, en utilisant de meilleurs noms de variables et en remplaçant nombres magiques avec des constantes nommées (ou, dans le cas de Python, des « constantes ») sont toutes de grandes choses. Mais à quand remonte la dernière fois où vous avez recherché des entrées implicites dans votre code et les avez rendues explicites ?
Combien d’entrées et de sorties la fonction suivante a-t-elle ?
find_big_numbers()
n’a pas de paramètres et retourne toujours None
. Si vous ne pouviez pas voir le corps de la fonction et ne pouviez pas accéder au flux de sortie standardcroiriez-vous même que cette fonction fait quelque chose ?
Et encore, find_big_numbers()
a deux entrées et une autre sortie en plus None
:
numbers.txt
est une entrée implicite. La fonction ne fonctionnera pas sans lui, mais il est impossible de savoir que le fichier est requis sans lire le corps de la fonction.- Le chiffre magique
100
sur la ligne 6 est une entrée implicite. Vous ne pouvez pas définir un « grand nombre » sans lui, mais il n’y a aucun moyen de connaître ce seuil sans lire le corps de la fonction. - Les valeurs peuvent ou non s’imprimer sur
stdout
selon le contenu denumbers.txt
. Il s’agit d’une sortie implicite car la fonction ne renvoie pas ces valeurs.
Les sorties implicites sont souvent appelées Effets secondaires.
Essayez-le vous-même
Identifier toutes les entrées et sorties du is_adult
fonction dans cet extrait de code :
Une bonne raison est leur penchant pour la violation de la principe de moindre surprise.
Bien sûr, toutes les entrées et sorties implicites ne sont pas mauvaises. Une méthode comme .write()
, que les objets de fichier Python utilisent pour écrire des données dans un fichier, a une sortie implicite : le fichier. Il n’y a aucun moyen de l’éliminer. Mais ce n’est pas surprenant. L’écriture dans un fichier est tout l’intérêt.
Par contre, une fonction comme is_adult()
de l’extrait de code précédent fait beaucoup de choses surprenantes. Les exemples moins extrêmes abondent.
Pointe: C’est un bon exercice pour lire une partie du code de votre bibliothèque préférée sur GitHub et voir si vous pouvez repérer les entrées et les sorties implicites. Demandez-vous : est-ce que l’un d’entre eux vous surprend ?
Éviter les entrées et sorties implicites améliore également la testabilité et la réutilisabilité de votre code. Pour voir comment, refactorisons le find_big_numbers()
fonction d’avant.
Voici find_big_numbers()
à nouveau pour ne pas avoir à faire défiler vers le haut :
Plus tôt, nous avons identifié deux entrées implicites, la numbers.txt
fichier et le numéro 100
et une sortie implicite, les valeurs imprimées sur stdout
. Travaillons d’abord sur les entrées.
Vous pouvez déplacer le nom du fichier et la valeur du seuil vers les paramètres de la fonction :
Cela a déjà considérablement amélioré la testabilité et la réutilisabilité. Si vous voulez l’essayer sur un autre fichier, passez le chemin comme argument. (En prime, le fichier peut désormais se trouver n’importe où sur votre ordinateur.) Vous pouvez également modifier le seuil pour les « grands nombres », si nécessaire.
Mais la sortie est difficile à tester.
Si vous voulez savoir que la fonction a produit les valeurs correctes, vous devez intercepter stdout
. C’est possible. Mais pourquoi ne pas simplement renvoyer une liste de toutes les valeurs :
À présent find_big_numbers()
a un caractère explicite return
instruction qui renvoie une liste de grands nombres trouvés dans le fichier.
Vous pouvez tester find_big_numbers()
en l’appelant avec le chemin d’un fichier dont le contenu est connu et en comparant la liste renvoyée à la liste des valeurs correctes :
find_big_numbers()
est plus réutilisable maintenant aussi. Vous n’êtes pas limité à l’impression des numéros sur stdout
. Vous pouvez envoyer ces gros chiffres où vous voulez.
Les entrées implicites sont des données utilisées par une fonction ou un programme qui ne sont pas explicitement transmises en tant qu’arguments. Vous pouvez éliminer les entrées implicites en les refactorisant en paramètres.
Les sorties implicites sont des données envoyées quelque part en dehors de la fonction ou du programme qui ne sont pas renvoyées explicitement. Vous pouvez supprimer les sorties explicites en les remplaçant par des valeurs de retour appropriées.
Toutes les entrées et sorties implicites ne peuvent pas être évitées, telles que les fonctions dont le but est de lire ou d’écrire des données à partir de fichiers et de bases de données ou d’envoyer un courrier électronique. Néanmoins, l’élimination d’autant d’entrées et de sorties implicites que possible améliore la testabilité et la réutilisabilité de votre code.
Voici donc la question à retenir : Avons-nous supprimé toutes les entrées et sorties implicites de find_big_numbers()
?