2010-03-11 10 views
22

estoy encontrando a mí mismo haciendo lo siguiente un poco demasiado a menudo:forma concisa a GetAttr() y usarlo si no es ninguno 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 

¿Existe una manera más Pythonic de escribir eso? ¿Es esto mejor? (Por lo menos no en el rendimiento, la OMI.)

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

"Al menos no en el rendimiento, la OMI". Probablemente deberías medirlo. Descubrirá que las excepciones de Python son muy rápidas. –

+1

Si se trata de un cuello de botella (probablemente no), el rendimiento también depende del caso común: ¿el atributo suele existir o, por lo general, no existe? – orip

Respuesta

26

Como llama al attr, puede usar d acaba de hacer:

def default_action(): 
    # do something else 

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

action() 
+0

¡Nunca pensé en esto, pero esto funciona perfectamente para algunos de mis casos de uso! –

5

Hay otro lenguaje agradable:

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

De las dos opciones que posteaste Yo prefiero la primera. La noción de arrojar excepciones al acceder a miembros simplemente no parece ser el objetivo para el que se utilizaron las excepciones.

+0

-1 ya que AttributeError es una excepción muy obvia que se utilizará "al acceder a los miembros". –

+2

Esto es una especie de pregunta abierta. No creo que usar 'AttributeError' como una buena práctica en esta situación, lo haces. ¿Significa que tienes razón y yo no? No lo creo – pajton

1

El uso de try/except generalmente se considera más pitónico. También es más eficiente si obj tiene el atributo, ya que elimina la prueba: los bloques de prueba no tienen sobrecarga si no se lanza la excepción. Por otro lado, es menos eficiente si obj no tiene el atributo, ya que tendrá la sobrecarga de tirar y atrapar la excepción.

+2

Quizás es pitónico, pero es feo y creo que las excepciones no son para ese tipo de propósito. En Python, incluso puede capturar 'IndentationError', ¿es esto también pitónico para rodear un código que no está seguro de tener la sangría adecuada? – pajton

+0

¿Por qué no? Supongamos que está importando un módulo de complemento y el módulo no puede importar. La excepción lanzada dice por qué. Si estaba motivado para hacerlo, podría detectar el error y decirle al usuario exactamente cómo solucionarlo ("Limpiar el problema de sangrado en la línea X del archivo Y"). Aprecio este tipo de flexibilidad. –

+0

Y te desafío a "rodear un código" que no está apropiadamente sangrado con un bloque try ... Tienes que ser más indirecto que eso porque el analizador captará el problema antes de que se complete la estructura 'try:'. –

6

La alternativa try/except, a medida que codificarlo, accidentalmente pudieran causar una AttributeError causado por problemas en cualquier cosa que attr() está llamando, al ser capturados. Si desea codificar con try/except, lo cual es razonable y bastante buen rendimiento si es raro que el atributo que falta, utilice:

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

esto no causa "accidental captura" que el anterior. En cuanto a qué enfoque es preferible, generalmente sigo el enfoque de captura de excepciones: "EAFP", "es más fácil pedir perdón que permiso" (un lema atribuido a menudo a la Almirante Grace Hopper, la fuerza impulsora detrás de COBOL y CODASYL) definitivamente aplica bien a Python ;-).

0

Ah, depende del código exacto. Sus dos herramientas:

  • hasattr (obj, 'attr') return Verdadero si y solo si obj.attr existe.
  • getattr (obj, 'attr', OTHER_VALUE) devuelve obj.attr si es que existe, de lo contrario OTHER_VALUE
  • tratar a = obj.attr/excepto el fracaso()/else hacer_algo (a) cuando el rendimiento es mejor que la legibilidad.

Éstos son los casos más comunes:

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) 

Para entender mejor todo el proceso, mira este fragmento:

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 

que tiene esta salida:

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

Similar a la respuesta de Joe, pero más corto:

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

Sí, esto funciona. Todavía usaría el método try/except en la mayoría de los casos, porque es más fácil de leer para otras personas. – chhantyal

Cuestiones relacionadas