2009-03-31 5 views
29

¿Existe una manera más agradable de hacer lo siguiente:Declaraciones de prueba anidadas en python?

try: 
    a.method1() 
except AttributeError: 
    try: 
     a.method2() 
    except AttributeError: 
     try: 
      a.method3() 
     except AttributeError: 
      raise 

se ve bastante desagradable y que preferiría no hacerlo:

if hasattr(a, 'method1'): 
    a.method1() 
else if hasattr(a, 'method2'): 
    a.method2() 
else if hasattr(a, 'method3'): 
    a.method3() 
else: 
    raise AttributeError 

para mantener la máxima eficiencia.

+0

¿Has probado tu teoría de que la segunda opción es ineficiente? Me sorprendería si no fuera más eficiente que el primero. – Oddthinking

+0

Oddthinking es probablemente correcto. hasattr elimina la necesidad de que se levante una excepción. –

+1

En realidad, la implementación de hasattr() básicamente solo llama a getattr() y devuelve False si se produce una excepción; ver http://svn.python.org/view/python/tags/r254/Python/bltinmodule.c?view = markup – Miles

Respuesta

21

Tal vez usted podría intentar algo como esto:

def call_attrs(obj, attrs_list, *args): 
    for attr in attrs_list: 
     if hasattr(obj, attr): 
      bound_method = getattr(obj, attr) 
      return bound_method(*args) 

    raise AttributeError 

Usted lo llamaría así:

call_attrs(a, ['method1', 'method2', 'method3']) 

Este tratará de llamar a los métodos en el orden en que están en en la lista. Si quería pasar ningún argumento, sólo podía pasar a lo largo después de la lista de este modo:

call_attrs(a, ['method1', 'method2', 'method3'], arg1, arg2) 
4

¿Qué hay de encapsular las llamadas en una función?

def method_1_2_or_3(): 
    try: 
     a.method1() 
     return 
    except AttributeError: 
     pass 
    try: 
     a.method2() 
     return 
    except AttributeError: 
     pass 
    try: 
     a.method3() 
    except AttributeError: 
     raise 
+1

¿Por qué la parte "encapsulante"? solo el 'pase' es una idea bastante agradable, me parece a mí. – cregox

1

Si está utilizando objeto de nuevo cuño:

methods = ('method1','method2','method3') 
for method in methods: 
    try: 
     b = a.__getattribute__(method) 
    except AttributeError: 
     continue 
    else: 
     b() 
     break 
else: 
    # re-raise the AttributeError if nothing has worked 
    raise AttributeError 

Por supuesto, si usted no está' Con un objeto de estilo nuevo, puede intentar __dict__ en lugar de __getattribute__.

EDITAR: Este código podría ser un lío de gritos. Si no se encuentra __getattribute__ o __dict__, adivine qué tipo de error se plantea.

+0

Definitivamente use la función getattr() en lugar del método __getattribute__. – Miles

+0

No puedo entender por completo las ventajas relativas de getattr vs __getattribute__. Existen objetos para los cuales cualquiera elevará AttributeError y el otro funcionará. –

23

Un ligero cambio en el segundo parece bastante agradable y simple. Realmente dudo que usted notará ninguna diferencia de rendimiento entre los dos, y esto es un poco mejor que un intento anidada/exceptúa

def something(a): 
    for methodname in ['method1', 'method2', 'method3']: 
     try: 
      m = getattr(a, methodname) 
     except AttributeError: 
      pass 
     else: 
      return m() 
    raise AttributeError 

La otra forma es muy fácil de leer que hacer ..

def something(a): 
    try: 
     return a.method1() 
    except: 
     pass 

    try: 
     return a.method2() 
    except: 
     pass 

    try: 
     return a.method3() 
    except: 
     pass 

    raise AttributeError 

Mientras tanto, es muy obvio lo que hace la función ... El rendimiento realmente no debería ser un problema (si unas pocas instrucciones try/except reducen notablemente el script, probablemente haya un problema mayor con la estructura del script)

+1

Me gusta el segundo, ya que es muy legible y directo. Si el rendimiento es realmente un problema, es probable que el póster original esté haciendo algo mal. –

3

Una solución compacta:

getattr(a, 'method1', 
    getattr(a, 'method2', 
     getattr(a, 'method3')))() 
+1

Compacto, pero posiblemente incorrecto. Si 'a' tiene' method1' pero * does not * have 'method3', entonces esto fallará. El tercer argumento para 'getattr' se evalúa antes de invocar' getattr', lo que significa que este código intenta obtener 'method3' antes de considerar' method1' y 'method2'. Ver [la respuesta de Ethan Furman] (http://stackoverflow.com/a/7971414/33732) para una alternativa más segura. –

5
method = (
     getattr(a, 'method1', None) or 
     getattr(a, 'method2', None) or 
     getattr(a, 'method3') 
     ) 
method() 

Este buscará primero method1, entonces method2, entonces method3. La búsqueda se detendrá tan pronto como se encuentre uno de ellos. Si no se encuentra ninguno de los métodos, el último getattr generará una excepción.

Cuestiones relacionadas