C'est moins une question sur la nature de la frappe de canard que sur la façon de rester pythonique, je suppose.
Tout d'abord - lorsqu'il s'agit de dictés, en particulier lorsque la structure du dict est assez prévisible et qu'une clé donnée n'est généralement pas présente mais l'est parfois, je pense d'abord à deux approches:
if myKey in dict:
do_some_work(dict[myKey])
else:
pass
Et bien sûr, l'approche du pardon contre la permission de Ye Olde.
try:
do_some_work(dict[myKey])
except KeyError:
pass
En tant que compagnon gars Python, j'ai l'impression que je vois ce dernier beaucoup préféré, ce qui ne me semble étrange que parce que dans les documents Python try/excepts
semblent être préférés quand il y a une erreur réelle, par opposition à une, euh ... absence de succès?
Si le dict occasionnel n'a pas de clé dans myDict, et on sait qu'il n'aura pas toujours cette clé, un essai / sauf contextuellement trompeur? Ce n'est pas une erreur de programmation, c'est juste un fait des données - ce dict n'a tout simplement pas cette clé particulière.
Cela semble particulièrement important lorsque vous regardez la syntaxe try / except / else, qui semble être vraiment utile lorsqu'il s'agit de s'assurer que try ne détecte pas trop d'erreurs. Vous pouvez faire quelque chose comme:
try:
foo += bar
except TypeError:
pass
else:
return some_more_work(foo)
Cela ne va-t-il pas conduire à avaler toutes sortes d'erreurs étranges qui sont probablement le résultat d'un mauvais code? Le code ci-dessus pourrait simplement vous empêcher de voir que vous essayez d'ajouter 2 + {}
et vous ne réaliserez peut-être jamais qu'une partie de votre code a horriblement mal tourné. Je ne suggère pas que nous devrions vérifier tous les types, c'est pourquoi c'est Python et non JavaScript - mais encore une fois avec le contexte de try / except, il semble qu'il est censé attraper le programme en train de faire quelque chose qu'il ne devrait pas faire, à la place de lui permettre de continuer.
Je me rends compte que l'exemple ci-dessus est quelque chose d'un argument de l'homme de paille, et est en fait intentionnellement mauvais. Mais étant donné le credo pythonique de, better to ask forgiveness than permission
je ne peux m'empêcher de penser que cela pose la question de savoir où se situe réellement la ligne entre l'application correcte de if / else vs try / except, en particulier lorsque vous savez à quoi vous attendre de les données avec lesquelles vous travaillez.
Je ne parle même pas des problèmes de vitesse ou des meilleures pratiques ici, je suis juste un peu confus par le diagramme Venn perçu des cas où il semble que cela pourrait aller dans les deux sens, mais les gens se trompent du côté d'un essai / sauf parce que «quelqu'un quelque part a dit que c'était Pythonic». Ai-je tiré des conclusions erronées sur l'application de cette syntaxe?
Réponses:
Utilisez plutôt la méthode get :
... où
default_value
est la valeur renvoyée sithe_key
n'est pas danssome_dict
. Si vous omettezdefault_value
,None
est retourné si la clé est manquante.En général, en Python, les gens ont tendance à préférer essayer / sauf plutôt que de vérifier d'abord quelque chose - voir l' entrée EAFP dans le glossaire . Notez que de nombreuses fonctions de "test d'appartenance" utilisent des exceptions en arrière-plan.
la source
dict
et de faire un travail basé sur ce qui est trouvé, il semble queif myDict.get(a_key) is not None: do_work(myDict[a_key])
c'est plus verbeux et encore un autre exemple de recherche implicite avant de sauter - en plus c'est à mon humble avis légèrement moins lisible. Peut-être que je ne comprends pasdict.get(a_key, a_default)
dans le bon contexte, mais il semble que ce soit un précurseur de l'écriture de 'not-a-switch-statement if / elses' ou de valeurs magiques. J'aime bien ce que fait cette méthode, je vais approfondir.data = myDict.get(a_key, default)
et soit vousdo_work
ferez la bonne chose une fois donnédefault
(par exemple.None
) Soit vous le ferezif data: do_work(data)
. Une seule recherche est nécessaire dans les deux cas. (Il se peutif data is not None: ...
, dans les deux cas, qu'il ne s'agisse que d'une seule recherche et que votre propre logique puisse prendre le relais.)try/except/else
déchets indésirables et a globalement amélioré à mon humble avis la lisibilité de mon code. J'ai appris une leçon précieuse que j'avais entendue auparavant mais j'ai réussi à négliger de prendre à cœur: "Si vous avez l'impression d'écrire trop de code, arrêtez-vous et regardez les fonctionnalités du langage." Merci!!La clé manquante est-elle une règle ou une exception ?
D'un point de vue pragmatique, lancer / attraper est beaucoup plus lent que de vérifier si la clé est dans le tableau. Si la clé manquante est un phénomène courant - vous devez utiliser la condition.
Une autre chose en faveur de la condition est une clause else - il est beaucoup plus facile de comprendre quand un bloc est exécuté, sachez que même la même classe d'exception peut être levée à partir de plusieurs instructions dans le bloc try.
Le code dans la clause except ne devrait pas faire partie de la faille normale (par exemple si la clé n'est pas là, ajoutez-la) - il devrait gérer l'erreur.
la source
try
/catch
plus rapidement.Dans l'esprit d'être "pythonique" et, plus précisément, de taper du canard - le try / except me semble presque fragile.
Tout ce que vous voulez vraiment, c'est quelque chose avec un accès de type dict, mais vous
__getitem__
pouvez le remplacer pour faire quelque chose qui ne vous convient pas. Par exemple, en utilisantdefaultdict
la bibliothèque standard:Comme la valeur de retour par défaut est Falsy, la vérification d'accès comme celle-ci fonctionnera très bien la plupart du temps.
Malheureusement, quelques mois plus tard, ces clés supplémentaires ont fini par provoquer des problèmes et des bogues difficiles à retrouver, car elles sont
0
devenues une valeur valide. Et parfois , nous pourrions utiliserin
pour vérifier si une clé existe, ce qui rend le code faire des choses différentes quand il aurait été identique.La raison pour laquelle je suggère que cela semble cassé est que, dans l'esprit de Python de la frappe de canard, tout ce que vous devriez vouloir est quelque chose de semblable à un dict, et
defaultdict
répond parfaitement à cette exigence, comme le ferait tout objet que vous pourriez créer qui hérite dedict
. Vous ne pouvez pas garantir que l'appelant ne changera pas son implémentation plus tard, vous devriez donc faire la chose avec le moins d'effets secondaires.Utilisez la première version
if myKey in mydict
,.Notez également qu'il s'agit spécifiquement de l'exemple de la question. Le credo en python de "Mieux vaut demander pardon que permission" est largement destiné à garantir que vous agissez réellement correctement lorsque vous n'obtenez pas ce que vous voulez. Par exemple, vérifier si un fichier existe et essayer de le lire - au moins 3 choses auxquelles je peux penser du haut de ma tête peuvent mal tourner:
Le premier sur lequel vous pouvez essayer de "demander la permission", mais par nature des conditions de course, cela ne fonctionnera pas nécessairement toujours; le second est trop facile à oublier de vérifier; et le troisième ne peut pas être vérifié sans essayer de lire le fichier. Dans tous les cas, ce que vous ferez après avoir échoué sera presque certainement le même, donc dans cet exemple, il est préférable de "demander pardon" en utilisant un try / except.
la source