2009-03-31 2 views
29

Есть ли лучше способ сделать следующее:Вложенные инструкции try в python?

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

Это выглядит довольно противно, и я предпочел бы не делать:

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

для поддержания максимальной эффективности.

+0

Вы протестировали свою теорию о том, что второй вариант неэффективен? Меня это удивило бы, если бы оно не было более эффективным, чем первое. – Oddthinking

+0

Нечеткое мышление, скорее всего, верно. hasattr устраняет необходимость возбуждения исключения. –

+1

На самом деле реализация hasattr() по сути просто вызывает getattr() и возвращает False, если возникает исключение; см. http://svn.python.org/view/python/tags/r254/Python/bltinmodule.c?view = markup – Miles

ответ

21

Может быть, вы могли бы попробовать что-то вроде этого:

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 

Вы назвали бы это так:

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

Это будет пытаться вызвать методы в том порядке, в которой они находятся в список. Если вы хотите, чтобы передать какие-либо аргументы, вы могли бы просто передать их вместе после списка, как так:

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

Как насчет инкапсуляции вызовов в функцию?

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

Почему «инкапсулирующая» часть? Мне кажется, что просто 'pass' - неплохая идея. – cregox

1

Если вы используете объект нового стиля:

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 

Конечно, если вы Арен» t с использованием объекта нового стиля, вы можете попробовать __dict__ вместо __getattribute__.

EDIT: Этот код может оказаться кричащим беспорядком. Если __getattribute__ или __dict__ не найден, подумайте, какая ошибка возникает.

+0

Определенно используйте функцию getattr() вместо метода __getattribute__. – Miles

+0

Я не могу полностью выяснить относительные преимущества getattr vs __getattribute__. Существуют объекты, для которых либо будет повышаться AttributeError, а другой будет работать. –

23

Небольшое изменение ко второму выглядит довольно красиво и просто. Я очень сомневаюсь, вы заметите разницу в производительности между этими двумя, и это немного лучше, чем вложенной попытки/excepts

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

Другой очень читаемый способ это сделать ..

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

    try: 
     return a.method2() 
    except: 
     pass 

    try: 
     return a.method3() 
    except: 
     pass 

    raise AttributeError 

Хотя долгое время, очень очевидно, что делает эта функция. Производительность действительно не должна быть проблемой (если несколько попыток/исключений значительно замедляют ваш сценарий, возможно, большая проблема со структурой сценария)

+1

Мне нравится вторая, так как она очень читаема и прямолинейна. Если производительность действительно является проблемой, исходный плакат, вероятно, делает что-то неправильно. –

3

Компактное решение:

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

Компактный, но, возможно, неправильный. Если 'a' имеет' method1', но * не имеет 'method3', то это не удастся. Третий аргумент 'getattr' оценивается до вызова' getattr', что означает, что этот код пытается извлечь 'method3', прежде чем он когда-либо рассмотрит' method1' и 'method2'. См. [Ответ Этана Фурмана] (http://stackoverflow.com/a/7971414/33732) для более безопасной альтернативы. –

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

Это первый ищет method1, то method2, то method3. Поиск будет остановлен, как только один из них будет найден. Если ни один из методов не найден, последний getattr вызовет исключение.

Смежные вопросы