J'écris mon propre conteneur, qui doit donner accès à un dictionnaire à l'intérieur par des appels d'attribut. L'utilisation typique du conteneur serait comme ceci:
dict_container = DictContainer()
dict_container['foo'] = bar
...
print dict_container.foo
Je sais qu'il peut être stupide d'écrire quelque chose comme ça, mais c'est la fonctionnalité que je dois fournir. Je pensais à mettre en œuvre cela de la manière suivante:
def __getattribute__(self, item):
try:
return object.__getattribute__(item)
except AttributeError:
try:
return self.dict[item]
except KeyError:
print "The object doesn't have such attribute"
Je ne sais pas si les blocs try / except imbriqués sont une bonne pratique, donc une autre façon serait d'utiliser hasattr()
et has_key()
:
def __getattribute__(self, item):
if hasattr(self, item):
return object.__getattribute__(item)
else:
if self.dict.has_key(item):
return self.dict[item]
else:
raise AttributeError("some customised error")
Ou pour en utiliser un et essayer un bloc catch comme ceci:
def __getattribute__(self, item):
if hasattr(self, item):
return object.__getattribute__(item)
else:
try:
return self.dict[item]
except KeyError:
raise AttributeError("some customised error")
Quelle option est la plus pythonique et élégante?
if 'foo' in dict_container:
. Amen.Réponses:
Votre premier exemple est parfaitement bien. Même les documents officiels de Python recommandent ce style appelé EAFP .
Personnellement, je préfère éviter d'imbriquer quand ce n'est pas nécessaire:
PS.
has_key()
est obsolète depuis longtemps dans Python 2. Utilisez-le à laitem in self.dict
place.la source
return object.__getattribute__(item)
est incorrect et produira unTypeError
car un nombre incorrect d'arguments est transmis. Cela devrait plutôt l'êtrereturn object.__getattribute__(self, item)
.from None
signifie la dernière ligne?Alors qu'en Java c'est en effet une mauvaise pratique d'utiliser les exceptions pour le contrôle de flux (principalement parce que les exceptions forcent le jvm à rassembler des ressources ( plus ici )), en Python vous avez 2 principes importants: Duck Typing et EAFP . Cela signifie essentiellement que vous êtes encouragé à essayer d'utiliser un objet de la façon dont vous pensez qu'il fonctionnerait, et à gérer lorsque les choses ne sont pas comme ça.
En résumé, le seul problème serait que votre code soit trop indenté. Si vous en avez envie, essayez de simplifier certains des emboîtements comme suggéré par lqc
la source
Pour votre exemple spécifique, vous n'avez pas réellement besoin de les imbriquer. Si l'expression dans le
try
bloc réussit, la fonction retournera, donc tout code après le bloc try / except entier ne sera exécuté que si la première tentative échoue. Vous pouvez donc simplement faire:Les imbriquer n'est pas mauvais, mais j'ai l'impression que le laisser à plat rend la structure plus claire: vous essayez séquentiellement une série de choses et retournez la première qui fonctionne.
Soit dit en passant, vous voudrez peut-être vous demander si vous voulez vraiment utiliser
__getattribute__
plutôt__getattr__
qu'ici. L'utilisation__getattr__
simplifiera les choses, car vous saurez que le processus de recherche d'attribut normal a déjà échoué.la source
Faites juste attention - dans ce cas,
finally
on touche d'abord MAIS sauté aussi.la source
finally
blocs sont exécutés poura(0)
, mais seul le parentfinally-return
est retourné.À mon avis, ce serait la façon la plus pythonique de le gérer, bien que cela rende votre question sans objet. Notez que cela définit
__getattr__()
au lieu de__getattribute__()
parce que cela signifie qu'il n'a à gérer que les attributs "spéciaux" conservés dans le dictionnaire interne.la source
except
bloc peut donner une sortie déroutante dans Python 3. C'est parce que (selon PEP 3134) Python 3 suit la première exception (laKeyError
) comme le "contexte" de la deuxième exception (laAttributeError
), et si elle atteint le de niveau supérieur, il imprimera une trace qui inclut les deux exceptions. Cela peut être utile lorsqu'une deuxième exception n'était pas attendue, mais si vous soulevez délibérément la deuxième exception, ce n'est pas souhaitable. Pour Python 3.3, PEP 415 a ajouté la possibilité de supprimer le contexte en utilisantraise AttributeError("whatever") from None
.KeyError
enAttributeError
et montre que ce qui s'est passé dans un retraçage serait utile et approprié.__getattr__
déclenche une exception, le bogue est probablement une faute de frappe dans l'accès aux attributs, pas un bogue d'implémentation dans le code de la classe actuelle. Montrer l'exception précédente comme contexte peut brouiller cela. Et même lorsque vous supprimez le contexte avecraise Whatever from None
, vous pouvez toujours obtenir l'exception précédente si nécessaire viaex.__context__
.__getattribute__()
.En Python, il est plus facile de demander pardon que permission. Ne transpirez pas la gestion des exceptions imbriquées.
(De plus,
has*
il utilise presque toujours des exceptions sous le couvert de toute façon.)la source
Selon la documentation , il est préférable de gérer plusieurs exceptions via des tuples ou comme ceci:
la source
Un bon et simple exemple d'imbrication try / except pourrait être le suivant:
Essayez maintenant différentes combinaisons et vous obtiendrez le résultat correct:
[bien sûr, nous avons numpy donc nous n'avons pas besoin de créer cette fonction]
la source
Une chose que j'aime éviter est de lever une nouvelle exception lors de la gestion d'une ancienne. Cela rend les messages d'erreur déroutants à lire.
Par exemple, dans mon code, j'ai écrit à l'origine
Et j'ai reçu ce message.
Ce que je voulais c'était:
Cela n'affecte pas la façon dont les exceptions sont gérées. Dans l'un ou l'autre bloc de code, une erreur KeyError aurait été interceptée. Il s'agit simplement d'obtenir des points de style.
la source
from None
, pour encore plus de points de style. :)Si try-except-finally est imbriqué dans le bloc finally, le résultat de "l'enfant" est finalement conservé. Je n'ai pas encore trouvé d'expiration officielle, mais l'extrait de code suivant montre ce comportement dans Python 3.6.
la source
Je ne pense pas qu'il s'agisse d'être pythonique ou élégant. Il s'agit d'empêcher autant que possible les exceptions. Les exceptions sont destinées à gérer les erreurs pouvant survenir dans le code ou les événements sur lesquels vous n'avez aucun contrôle. Dans ce cas, vous avez un contrôle total lorsque vous vérifiez si un élément est un attribut ou dans un dictionnaire, évitez donc les exceptions imbriquées et respectez votre deuxième tentative.
la source