“Demander pardon de ne pas l'autorisation” - expliquer
Je ne demande pas à des fins personnelles "religieux" avis au sujet de cette philosophie, plutôt quelque chose d'un peu plus de technique.
Je comprends cette phrase est l'un de plusieurs de tournesol tests pour voir si votre code est "pythonic". Mais pour moi, pythonic signifie propre, simple et intuitive, pas chargé avec des gestionnaires d'exception pour les mauvais codage.
Donc, à l'exemple pratique. Je définir une classe:
class foo(object):
bar = None
def __init__(self):
# a million lines of code
self.bar = "Spike is my favorite vampire."
# a million more lines of code
Maintenant, venir à partir d'un contexte procédural, dans une autre fonction que je veux faire ceci:
if foo.bar:
# do stuff
Je vais obtenir un attribut exception si j'étais impatient et n'a pas faire les premiers foo = None. Donc, "demander pardon de ne pas l'autorisation" donne à penser que je devrais faire à la place?
try:
if foo.bar:
# do stuff
except:
# this runs because my other code was sloppy?
Pourquoi il serait mieux pour moi d'ajouter une logique supplémentaire dans un bloc try juste pour que je puisse laisser ma définition de la classe plus ambigu? Pourquoi ne pas définir tout d'abord, donc explicitement accorder l'autorisation?
(Ne me battre sur l'utilisation de blocs try/except... je l'utilise partout. Je ne pense pas qu'il est juste de les utiliser pour attraper mes propres erreurs car je n'ai pas approfondi programmeur.)
Ou... dois-je totalement méconnaître le "Demander Forgivess" mantra?
- Votre déclaration "n'est pas chargé avec des gestionnaires d'exception pour les mauvais codage" est un non sequitur. Bon code a tous à l'exception de la manipulation nécessaire, mais pas un peu plus. Et la présence de gestionnaires d'exceptions à peine implique de "mauvais codage." Je pense que sa serait préférable de laisser l'application gérer un problème si possible (si pour aucune autre raison que de fermer gracieusement), plutôt que de permettre qu'il se bloque sans ménagement dans les genoux de l'utilisateur 🙂
- Vous ne devriez pas essayer d'ajouter des gestionnaires d'exception pour attraper les mauvais codage, et surtout pas un nu
except:
clause. Vos tests unitaires doivent attraper ces problèmes, et il n'y a rien de mal avec l'exception de cracher une traçabilité en amont et en tuant l'ensemble du processus pour ce genre d'erreur. - Un autre avantage de demander pardon, c'est qu'il peut parfois éviter des bugs que l'autorisation ne peut tout simplement pas, lorsque vous faites affaire avec de dynamique hors de votre contrôle. Je pense que l'exemple canonique est
os.path.exists
, qui vous indique seulement que le fichier existe ou n'existe pas, à un certain point. Probablement 90%+ deif os.path.exists(filename): do_something_to(filename)
les usages sont techniquement buggy. - ou tout simplement
open()
- ne pas vérifier pour voir si vous pouvez ouvrir le fichier en premier (autorisations, à l'existence, etc...) - il suffit de l'essayer 🙂 [qui évite des conditions de course]
Vous devez vous connecter pour publier un commentaire.
Le classique "demander pardon de ne pas l'autorisation" exemple est l'accès à des valeurs à partir d'un
dict
qui peut ne pas exister. E. g.:Ici l'exception qui pourrait se produire (
KeyError
) est indiqué, de sorte que vous n'êtes pas à demander pardon pour toutes les erreurs qui peuvent se produire, mais seulement celui qui se produisent naturellement.Pour comparaison, le "demander la permission d'abord" pourrait ressembler à:
ou
De tels exemples de "demander pardon" sont souvent trop simples. IMO c'est pas limpide que
try
/except
blocs sont intrinsèquement meilleure queif
/else
. La valeur réelle est beaucoup plus claire lors de l'exécution d'opérations qui peuvent échouer dans une variété de moyens, tels que l'analyse; à l'aide deeval()
, l'accès au système d'exploitation, middleware, base de données, ou de ressources en réseau, ou en effectuant des mathématiques complexes. Lorsqu'il y a plusieurs modes de défaillance potentiels, être prêt à obtenir le pardon est très précieuse.D'autres notes au sujet de vos exemples de code:
Vous n'avez pas besoin de louche
try
/except
les blocs autour de chaque variable d'utilisation. Ce serait horrible. Et vous n'avez pas besoin deself.bar
dans votre__init__()
depuis qu'il est défini dans votreclass
définition ci-dessus. Il est habituel de définir dans la classe (si c'est des données susceptibles d'être partagée entre toutes les instances de la classe) ou dans le__init__()
(si c'est des données d'instance, spécifiques à chaque instance).Une valeur de
None
n'est pas défini, ou une erreur, par la manière. C'est un spécifique et légitime la valeur, le sens aucun, nul, nul, ou rien. Beaucoup de langues ont de telles valeurs, les programmeurs ne pas "surcharger"0
,-1
,''
(chaîne vide) ou similaire valeurs utiles.“Demander pardon, pas la permission” oppose deux styles de programmation. “Demander l'autorisation” va comme ceci:
“Demander pardon” va comme ceci:
C'est une situation où il est prévu que d'essayer d'effectuer l'opération peut échouer, et vous avez à gérer la situation où l'opération est impossible, d'une manière ou d'une autre. Par exemple, si l'opération est de l'accès à un fichier, le fichier peut ne pas exister.
Il y a deux raisons principales pour lesquelles il est préférable de demander pardon:
can_do_operation()
et le temps lorsque vous exécutezperform_operation()
. Donc, vous avez à gérer l'erreur de toute façon.Ce demander pardon situations ont en commun, c'est que vous tentez d'effectuer une opération, et vous savez que l'opération peut échouer.
Lorsque vous écrivez
foo.bar
, la non-existence debar
n'est pas normalement considéré comme un échec de l'objetfoo
. C'est habituellement un programmeur d'erreur: tentative d'utilisation d'un objet de façon qu'il n'a pas été conçu pour. La conséquence d'une erreur du développeur Python est un non gérée exception (si vous avez de la chance: bien sûr, certaines erreurs du développeur ne peut pas être détecté automatiquement). Donc, sibar
est un élément facultatif de l'objet, la manière habituelle de traiter cette question est d'avoir unebar
champ est initialisé àNone
, et une autre valeur si l'option est présente. Pour tester sibar
est présent, écrireVous pouvez abréger
if foo.bar is not None:
àif foo.bar:
seulement sibar
sera toujours vrai que si elle est interprétée comme une valeur de type boolean — sibar
pourrait être 0,[]
,{}
ou tout autre objet qui a une fausse valeur de vérité, vous avez besoin de lais not None
. Il est également plus clair, si vous faites des tests pour une partie facultative (par opposition à des tests entreTrue
etFalse
).À ce stade, vous vous demandez peut-être pourquoi ne pas omettre l'initialisation de
bar
quand il n'y est pas, et de tester sa présence avechasattr
ou de l'attraper avec uneAttributeError
gestionnaire? Parce que votre code n'a de sens que dans deux cas:bar
champ;bar
champ, cela signifie que ce que vous pensez que cela signifie.Donc, lors de l'écriture ou de décider de l'utilisation de l'objet, vous devez vous assurer que ce n'est pas un
bar
de terrain avec un sens différent. Si vous devez utiliser un autre objet qui n'a pas debar
champ, ce n'est probablement pas la seule chose que vous aurez besoin de s'adapter, de sorte que vous aurez probablement envie de faire une classe dérivée ou d'encapsuler l'objet dans un autre.except
clauses, et j'ai eu à traiter avec des scores de difficile-à-trouver des bugs comme un résultat. De toute façon, je pense que c'est la meilleure réponse.Vous avez le droit-le but de
try
etexcept
ne sont pas à couvrir votre bâclée de codage. Qui mène juste à la plus bâclée de codage.La gestion des exceptions devraient être utilisés pour gérer des circonstances exceptionnelles (bâclée de codage n'est pas une circonstance exceptionnelle). Cependant, souvent, il est facile de prédire que des circonstances exceptionnelles peuvent se produire. (par exemple, un votre programme accepte des entrées de l'utilisateur et l'utilise pour accéder à un dictionnaire, mais l'entrée de l'utilisateur n'était pas une clé dans le dictionnaire ...)
Il ya beaucoup de bonnes réponses ici, j'ai juste pensé que je voudrais ajouter un point que je n'ai pas vu mentionnée.
Souvent à demander pardon au lieu de la permission a des améliorations de performances.
pour demander la permission chaque temps.
vous n'avez qu'à effectuer une opération supplémentaire parfois, c'est à dire quand
il échoue.
Habituellement, les cas sont rares, ce qui signifie que si vous êtes seulement en lui demandant la permission, alors vous avez à peine jamais avoir à faire n'importe des opérations supplémentaires.
Oui, lorsqu'elle échoue, elle renvoie une exception, ainsi que l'exécution d'une opération supplémentaire, mais des exceptions en python sont très rapides. Vous pouvez voir quelques timings ici: https://jeffknupp.com/blog/2013/02/06/write-cleaner-python-use-exceptions/
Mon personnel et non-religieux de l'opinion, c'est que dit le mantra s'applique principalement aux documenté et bien compris conditions de sortie et les cas limites (par exemple, erreurs d'e/S), et ne devrait jamais être utilisé comme un get-out-of-prison de la carte pour mauvaise programmation.
Cela dit,
try/except
est souvent utilisé lorsque le "mieux" que des alternatives existent. Par exemple:Comme pour ton exemple, n'utilisez
if hasattr(foo, "bar")
si vos n'avez aucun contrôle surfoo
et de la nécessité de vérifier la conformité avec vos attentes, sinon utilisez simplementfoo.bar
et de laisser l'erreur qui en résulte être votre guide pour l'identification et la résolution bâclée code.Dans le Python contexte de "Demander pardon de ne pas l'autorisation" implique un style de programmation où vous ne vérifiez pas les choses comme vous vous attendez préalablement, mais plutôt de vous traiter les erreurs que si elles ne le sont pas. L'exemple classique est de ne pas vérifier qu'un dictionnaire contient une clé donnée, comme dans:
Mais plutôt de gérer l'erreur qui en résulte si la clé est manquante:
Toutefois, le point n'est pas de remplacer tous les
if
dans votre code avec untry
/except
; ce serait faire de votre code décidément messier. Au lieu de cela, vous ne devez attraper les erreurs où vous pouvez vraiment faire quelque chose à leur sujet. Dans l'idéal, ce serait de réduire le montant de l'ensemble de la gestion des erreurs dans votre code, ce qui rend son objectif réel de plus en plus évidente.Alors qu'il y a déjà un certain nombre de haute qualité des réponses, plus principalement en discuter à partir d'un point de vue stylistique, comme apposer à une fonctionnelle.
Il y a certains cas où nous avons besoin de demander pardon, pas la permission d'assurer le code est correct (en dehors de programmes multithread).
Un exemple canonique étant,
Dans cet exemple, le fichier peut avoir été supprimé entre la case et en essayant d'ouvrir le fichier. C'est évitée en utilisant
try
:Cette culture dans une variété de lieux, souvent en collaboration avec des systèmes de fichiers ou les Api externe.
Demander pardon de ne pas l'autorisation est destinée à simplifier le code. Il est destiné pour le code à écrire comme ça quand il y a une attente raisonnable que
.bar
pourrait déclencher une AttributeError.Votre code apparaît à la fois demander la permission ET le pardon 🙂
La chose est, si vous pouvez raisonnablement s'attendre à quelque chose à l'échec, utiliser un try/catch. Si vous n'avez pas s'attendre à quelque chose à l'échec, et il échoue, en tout cas, bien l'exception qui obtient jeté devient l'équivalent d'un échec d'assertion dans d'autres langues. Vous voyez où l'exception inattendue est survenue et d'ajuster votre code/hypothèses en conséquence.
except
énoncés sont généralement considéré comme une mauvaise pratique. Seul hic, les exceptions que vous savez comment le gérer et rien de plus.AttributeError