Вложенные операторы try в python? - PullRequest
30 голосов
/ 31 марта 2009

Есть ли лучший способ сделать следующее:

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

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

Ответы [ 6 ]

24 голосов
/ 31 марта 2009

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

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

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

22 голосов
/ 31 марта 2009

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

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)
5 голосов
/ 01 ноября 2011
method = (
        getattr(a, 'method1', None) or
        getattr(a, 'method2', None) or
        getattr(a, 'method3')
        )
method()

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

4 голосов
/ 31 марта 2009

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

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
3 голосов
/ 10 марта 2011

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

getattr(a, 'method1',
    getattr(a, 'method2',
        getattr(a, 'method3')))()
1 голос
/ 31 марта 2009

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

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

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

РЕДАКТИРОВАТЬ: Этот код может оказаться кричащим беспорядок. Если __getattribute__ или __dict__ не найден, предположите, какая ошибка возникла.

...