"Demandez le pardon, pas la permission" s'oppose à deux styles de programmation. "Demandez la permission" se présente comme suit :
if can_do_operation():
perform_operation()
else:
handle_error_case()
"Demander pardon", c'est comme ça :
try:
perform_operation()
except Unable_to_perform:
handle_error_case()
Il s'agit d'une situation où l'on s'attend à ce que la tentative d'exécution de l'opération échoue, et vous devez gérer la situation où l'opération est impossible, d'une manière ou d'une autre. Par exemple, si l'opération consiste à accéder à un fichier, il se peut que le fichier n'existe pas.
Il y a deux raisons principales pour lesquelles il est préférable de demander pardon :
- Dans un monde concurrent (dans un programme multithread ou si l'opération implique des objets externes au programme tels que des fichiers, d'autres processus, des ressources de réseau, etc.
can_do_operation()
et le moment où vous exécutez perform_operation()
. Vous devrez donc traiter l'erreur de toute façon.
- Vous devez utiliser exactement les bons critères pour demander la permission. Si vous vous trompez, vous ne pourrez pas effectuer une opération que vous pourriez effectuer ou une erreur se produira parce que vous ne pourrez pas effectuer l'opération. Par exemple, si vous testez l'existence d'un fichier avant de l'ouvrir, il est possible que le fichier existe, mais que vous ne puissiez pas l'ouvrir parce que vous n'avez pas l'autorisation. À l'inverse, il se peut que le fichier soit créé lorsque vous l'ouvrez (par exemple parce qu'il provient d'une connexion réseau qui n'est activée que lorsque vous ouvrez réellement le fichier, et non lorsque vous vous contentez de vérifier s'il est présent).
Les situations de demande de pardon ont en commun le fait que vous tentez d'effectuer une opération, et que vous savez que l'opération peut échouer.
Quand vous écrivez foo.bar
, la non-existence de bar
n'est pas normalement considéré comme un échec de l'objet foo
. Il s'agit généralement d'une erreur de programmation : on tente d'utiliser un objet d'une manière pour laquelle il n'a pas été conçu. La conséquence d'une erreur du programmeur en Python est une non géré exception (si vous avez de la chance : bien sûr, certaines erreurs de programmeur ne peuvent pas être détectées automatiquement). Ainsi, si bar
est une partie facultative de l'objet, la façon normale de gérer cela est d'avoir un fichier de type bar
qui est initialisé à None
et défini à une autre valeur si la partie facultative est présente. Pour tester si bar
est présent, écrivez
if foo.bar is not None:
handle_optional_part(foo.bar)
else:
default_handling()
Vous pouvez abréger if foo.bar is not None:
a if foo.bar:
seulement si bar
sera toujours vrai lorsqu'il est interprété comme un booléen - si bar
pourrait être de 0, []
, {}
ou tout autre objet qui a une fausse valeur de vérité, vous avez besoin de l'option is not None
. Il est également plus clair, si vous effectuez un test pour une partie facultative (par opposition à un test entre True
y False
).
A ce stade, vous pouvez vous demander : pourquoi ne pas omettre l'initialisation de la fonction bar
lorsqu'il n'est pas là, et de tester sa présence à l'aide de hasattr
ou l'attraper avec un AttributeError
manipulateur ? Parce que votre code n'a de sens que dans deux cas :
- l'objet n'a pas
bar
champ ;
- l'objet a un
bar
champ qui signifie ce que vous pensez qu'il signifie.
Ainsi, lorsque vous écrivez ou décidez d'utiliser l'objet, vous devez vous assurer qu'il n'a pas d'objet de type bar
avec une signification différente. Si vous avez besoin d'utiliser un objet différent qui n'a pas d'objet bar
mais ce n'est probablement pas la seule chose que vous devrez adapter. Vous voudrez donc probablement créer une classe dérivée ou encapsuler l'objet dans une autre.