2010-03-11 3 views
22

Je me retrouve à faire ce qui suit un peu trop souvent:manière concise à getattr() et l'utiliser sinon aucun en Python

attr = getattr(obj, 'attr', None) 
if attr is not None: 
    attr() 
    # Do something, either attr(), or func(attr), or whatever 
else: 
    # Do something else 

est-il un moyen plus pythonique d'écrire cela? Est-ce mieux? (Au moins pas dans la performance, l'OMI.)

try: 
    obj.attr() # or whatever 
except AttributeError: 
    # Do something else 
+0

"Au moins pas dans la performance, l'OMI". Vous devriez probablement le mesurer. Vous trouverez que les exceptions Python sont très rapides. –

+1

S'il s'agit réellement d'un goulot d'étranglement (probablement pas), les performances dépendent également du cas courant: l'attribut existe-t-il habituellement ou n'existe-t-il généralement pas? – orip

Répondre

26

Depuis que vous appelez le attr, vous pouvez d il suffit de faire:

def default_action(): 
    # do something else 

action = getattr(obj, 'attr', default_action) 

action() 
+0

Je n'y avais jamais pensé, mais cela fonctionne parfaitement pour certains de mes cas d'utilisation! –

5

Il y a une autre belle expression:

if hasattr(obj, 'attr'): 
    ...something... 
else: 
    ...something else... 

Depuis les deux options que je préfère le posté premier. La notion de lancer des exceptions lors de l'accès aux membres ne semble pas être la raison pour laquelle les exceptions étaient censées être utilisées.

+0

-1 puisque AttributeError est une exception très évidente à utiliser "lors de l'accès aux membres". –

+2

C'est une question ouverte. Je ne trouve pas l'utilisation de 'AttributeError' comme une bonne pratique dans cette situation, vous le faites. Cela signifie-t-il que vous avez raison et que je ne le suis pas? Je ne le pense pas – pajton

1

L'utilisation de try/except est généralement considérée comme plus pythonique. Il est aussi plus efficace si obj a l'attribut, puisqu'il élimine le test - les blocs try n'ont pas de surcharge si l'exception n'est pas lancée. D'autre part, il est moins efficace si obj n'a pas l'attribut car il aura le temps de lancer et d'attraper l'exception.

+2

Peut-être que c'est pythonique, mais c'est moche et je crois que les exceptions ne sont pas faites pour ce genre de but. En python, vous pouvez même attraper 'IndentationError', est-ce aussi pythonique d'entourer du code dont vous n'êtes pas sûr qu'il est correctement indenté? – pajton

+0

Pourquoi n'est-ce pas? Supposons que vous importiez un module de plug-in et que le module ne puisse pas être importé. L'exception lancée dit pourquoi. Si vous étiez motivé à le faire, vous pourriez attraper l'erreur et dire à l'utilisateur exactement comment la réparer ("Effacer le problème d'indentation sur la ligne X du fichier Y"). J'apprécie ce genre de flexibilité. –

+0

Et je vous mets au défi d '"entourer du code" qui n'est pas correctement indenté avec un bloc try .... Vous devez être plus indirect que cela parce que l'analyseur va attraper le problème avant que la structure 'try:' soit complétée. –

6

Le try/except alternatif, comme vous le code il, pourrait accidentellement provoquer une AttributeError causée par des problèmes dans tout ce qui attr() appelle, d'être pris. Si vous voulez coder avec try/except, ce qui est raisonnable et tout à fait performant s'il est rare que l'attribut à manquer, utilisez:

try: 
    attr = obj.attr 
except AttributeError: 
    dosome(thingelse) 
else: 
    attr() 

cela pas la cause « accidentelle attraper » comme ci-dessus. Quant à l'approche qui est préférable, j'adopte généralement l'approche de l'exception - «EAFP», «il est plus facile de demander pardon que de permission» (une devise souvent attribuée à l'amiral Grace Hopper, force motrice de COBOL et CODASYL). bien à Python ;-).

0

Ah, cela dépend du code exact. Vos deux outils:

  • hasattr (obj, 'attr') return Vrai si et seulement si obj.attr existe.
  • getattr (obj, 'attr', OTHER_VALUE) retourne obj.attr si elle existe, sinon OTHER_VALUE
  • essayer a = obj.attr/sauf échec()/else do_something (a) lorsque la performance bat la lisibilité.

Voici les cas les plus courants:

the_name = getattr(user, 'name', '<Unknown User>') 
user.name = getattr(user, 'name', '<Unknown User>') 
if not hasattr(name, 'user'): 
    try_asking_again() 
name = user.name if hasattr(user, 'name') else do_expensive_name_lookup(user) 

Pour mieux comprendre le processus, regardez cet extrait:

class Thing(): 
    def __init__(self): 
     self.a = 'A' 

    def __getattr__(self, attr): 
     if attr == "b": 
      return "B" 
     else: 
      raise AttributeError("Thing instance has no attribute '" + attr + "'") 

item = Thing() 
print "hasattr(a) is " + str(hasattr(item, "a")) 
print "a is " + item.a 
print "hasattr(b) is " + str(hasattr(item, "b")) 
print "b is " + item.b 
out = "c is " + item.c if hasattr(item, "c") else "No C" 
print out 
print "and c is also " + getattr(item, "c", "Not Assigned") 
print "c throws an Attribute exception " + item.c 

qui a cette sortie:

hasattr(a) is True 
a is A 
hasattr(b) is True 
b is B 
No C 
and c is also Not Assigned 
Traceback (most recent call last): 
    File "attr_snippet.py", line 23, in <module> 
    print "c throws an Attribute exception " + item.c 
    File "attr_snippet.py", line 9, in __getattr__ 
    raise AttributeError("Thing instance has no attribute '" + attr + "'") 
AttributeError: Thing instance has no attribute 'c' 
5

similaires à la réponse de Joe, mais plus court:

getattr(obj, 'attr', lambda: None)() 
+0

Oui, cela fonctionne. J'utiliserais toujours l'approche try/except dans la plupart des cas, car il est plus facile de lire à d'autres personnes. – chhantyal

Questions connexes