Python попробуй еще


582

Для чего предназначено необязательное elseпредложение tryоператора?


1
Большинство ответов, кажется, концентрируются на том, почему мы не можем просто поместить материал в предложение else в самом предложении try. Вопрос stackoverflow.com/questions/3996329 специально спрашивает, почему код предложения else не может идти после самого блока try, и этот вопрос дублирован на этот, но я не вижу четкого ответа на этот вопрос здесь. Я чувствую, что stackoverflow.com/a/3996378/1503120 превосходно отвечает на этот вопрос. Я также попытался объяснить различную значимость различных статей на stackoverflow.com/a/22579805/1503120 .
Джамадагни

Вы хотите, чтобы что-то происходило, если перед окончательной очисткой не сработало исключение, которое само по себе не должно вызывать такую ​​же обработку исключений.
Бенджимин

Ответы:


858

Операторы в elseблоке выполняются, если выполнение падает в нижней части try- если не было исключений. Честно говоря, я никогда не нашел в этом необходимости.

Тем не менее, примечания по обработке исключений :

Использование предложения else лучше, чем добавление дополнительного кода к предложению try, поскольку оно позволяет избежать случайного перехвата исключения, которое не было вызвано кодом, защищаемым оператором try ... кроме.

Итак, если у вас есть метод, который может, например, выдать IOError, и вы хотите перехватывать исключения, которые он вызывает, но есть еще кое-что, что вы хотите сделать, если первая операция завершится успешно, и вы не хотите перехватывать IOError эта операция, вы можете написать что-то вроде этого:

try:
    operation_that_can_throw_ioerror()
except IOError:
    handle_the_exception_somehow()
else:
    # we don't want to catch the IOError if it's raised
    another_operation_that_can_throw_ioerror()
finally:
    something_we_always_need_to_do()

Если вы просто поставите another_operation_that_can_throw_ioerror()после operation_that_can_throw_ioerror, то exceptбудет ловить ошибки второго вызова. И если вы поставите его после целого tryблока, он всегда будет запущен, и только после finally. elseПозволяет убедиться ,

  1. вторая операция запускается, только если нет исключения,
  2. это бег перед finallyблоком, и
  3. любые IOErrors, которые это поднимает, не пойманы здесь

7
Также имейте в виду, что переменные, используемые в блоке try, могут использоваться в блоке else, поэтому вам следует всегда использовать этот вариант, если вы не ожидаете больше исключений в блоке else
WorldSEnder

3
Это не имеет значения, потому что переменные с пробной областью видны вне попытки, есть ли еще или нет.
Reinderien

36
Нет такой вещи как «переменная с пробной областью действия». В Python области видимости переменных создаются только модулями, функциями и представлениями, а не структурами управления.
Mhsmith

9
Предложение else позволяет вам написать код, который имеет смысл, только если исключение не было выдано; исключающее предложение может просто пройти. Если вы поместите логику в блок try, вы рискуете скрыть ошибки в своем коде. Никогда не подавляйте исключения, которые вы не ожидали.
Алиса Перселл,

9
из этого ответа неясно, что означает «падает от дна» - это происходит не только из-за исключения, но также из-за return, continueили break.
Антти Хаапала

108

Есть одна большая причина для использования else- стиль и удобочитаемость. Как правило, рекомендуется хранить код, который может вызывать исключения, рядом с кодом, который их обрабатывает. Например, сравните это:

try:
    from EasyDialogs import AskPassword
    # 20 other lines
    getpass = AskPassword
except ImportError:
    getpass = default_getpass

а также

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
else:
    # 20 other lines
    getpass = AskPassword

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

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
    return False  # or throw Exception('something more descriptive')

# 20 other lines
getpass = AskPassword

Примечание: Ответ скопирован с недавно отправленный дубликат здесь , следовательно , все эти вещи «AskPassword».


53

Одно использование: протестировать некоторый код, который должен вызвать исключение.

try:
    this_should_raise_TypeError()
except TypeError:
    pass
except:
    assert False, "Raised the wrong exception type"
else:
    assert False, "Didn't raise any exception"

(Этот код должен быть абстрагирован в более общий тест на практике.)


50

Python попробуй еще

Для чего предназначено необязательное elseпредложение оператора try?

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

Этот контекст позволяет избежать случайной обработки ошибок, которые вы не ожидали.

Но важно понять точные условия , которые вызывают еще пункт о перспективе, потому что return, continueи breakможет прервать поток управления в else.

В итоге

elseЗаявление работает , если нет ни одного исключения , и если не прерывается return, continueили breakзаявление.

Другие ответы пропускают эту последнюю часть.

Из документов:

Дополнительный elseпункт выполняется , если и когда управление оттекает в конце этого tryпункта. *

(Добавлен жирный шрифт.) И сноска гласит:

* В настоящее время, управление «оттекает конца» , за исключением того, в случае исключения или исполнения return, continueили breakзаявления.

Требуется хотя бы одно предшествующее, кроме предложения ( см. Грамматику ). Так что на самом деле это не «попробуй-иначе», это «попробуй-кроме-еще (-наконец)», с elsefinally ) является необязательным.

В Python Tutorial конкретизирует предполагаемого использования:

Оператор try ... кроме имеет необязательное предложение else, которое, если оно присутствует, должно следовать за всеми, кроме предложений. Это полезно для кода, который должен быть выполнен, если предложение try не вызывает исключение. Например:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'cannot open', arg
    else:
        print arg, 'has', len(f.readlines()), 'lines'
        f.close()

Использование предложения else лучше, чем добавление дополнительного кода к предложению try, поскольку оно позволяет избежать случайного перехвата исключения, которое не было вызвано кодом, защищаемым оператором try ... кроме.

Пример дифференцирования elseи кода послеtry блока

Если вы обработаете ошибку, elseблок не будет работать. Например:

def handle_error():
    try:
        raise RuntimeError('oops!')
    except RuntimeError as error:
        print('handled a RuntimeError, no big deal.')
    else:
        print('if this prints, we had no error!') # won't print!
    print('And now we have left the try block!')  # will print!

И сейчас,

>>> handle_error()
handled a RuntimeError, no big deal.
And now we have left the try block!

26

Try-кроме-еще отлично подходит для объединения шаблона EAFP с Duck-Typing :

try:
  cs = x.cleanupSet
except AttributeError:
  pass
else:
  for v in cs:
    v.cleanup()

Вы могли бы подумать, что этот наивный код подходит:

try:
  for v in x.cleanupSet:
    v.clenaup()
except AttributeError:
  pass

Это отличный способ случайно скрыть серьезные ошибки в вашем коде. Я опечатал там уборку, но ошибка AttributeError, которая дала бы мне знать, проглатывается. Хуже того, что если бы я написал это правильно, но метод очистки иногда передавал пользовательский тип, имеющий атрибут с неправильным именем, что приводило к молчаливому сбою на полпути и оставлению файла незамеченным? Удачи в отладке этого.


19

Я считаю, что это действительно полезно, когда нужно выполнить очистку, даже если есть исключение:

try:
    data = something_that_can_go_wrong()
except Exception as e: # yes, I know that's a bad way to do it...
    handle_exception(e)
else:
    do_stuff(data)
finally:
    clean_up()

9

Даже если вы не можете думать об использовании этого прямо сейчас, вы можете поспорить, что должно быть использование для этого. Вот невообразимый образец:

С else:

a = [1,2,3]
try:
    something = a[2]
except:
    print "out of bounds"
else:
    print something

Без else:

try:
    something = a[2]
except:
    print "out of bounds"

if "something" in locals():
    print something

Здесь у вас есть переменная, somethingопределенная, если нет ошибки. Вы можете удалить это вне tryблока, но тогда это требует некоторого беспорядочного обнаружения, если переменная определена.


3
Что не так something = a[2]; print somethingвнутри блока try: block?
С.Лотт

@ S.Lott ничего, но что, если кто-то отправляет вам список, и вы не хотите отображать данные, если они недостаточно длинные, потому что они, вероятно, повреждены?
неизвестно

12
С. Лотт: «напечатать что-нибудь» может вызвать другое исключение, которое вы не хотите перехватывать.
Дариус Бэкон

Я не вижу разницы. Если я получаю исключение «вне границ», оно печатает «вне границ». Понял. Если я получу какое-то другое исключение, оно не будет поймано этим блоком кода. Если я не получаю никаких исключений, поведение будет печатать значение чего-то, что является [2]. Я не вижу, что еще делает в этом примере.
S.Lott

3
Значение «что-то» при печати может вызвать ошибку в его методе __str __ (). Хотя в данном примере это значение на самом деле равно 2, с тем же успехом можно указать, что здесь нет и исключения за пределами допустимого диапазона.
Дариус Бэкон

8

Там хороший пример try-elseв PEP 380 . По сути, все сводится к разной обработке исключений в разных частях алгоритма.

Это что-то вроде этого:

try:
    do_init_stuff()
except:
    handle_init_suff_execption()
else:
    try:
        do_middle_stuff()
    except:
        handle_middle_stuff_exception()

Это позволяет вам писать код обработки исключений ближе к месту возникновения исключения.


7

Из ошибок и исключений # Обработка исключений - docs.python.org

try ... exceptЗаявление имеет дополнительный elseпункт, который, если присутствует, должен следовать все , за исключением положений. Это полезно для кода, который должен быть выполнен, если предложение try не вызывает исключение. Например:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'cannot open', arg
    else:
        print arg, 'has', len(f.readlines()), 'lines'
        f.close()

Использование предложения else лучше, чем добавление дополнительного кода к предложению try, поскольку оно позволяет избежать случайного перехвата исключения, которое не было вызвано кодом, защищаемым оператором try ... кроме.


6

Глядя на ссылку Python, кажется, что elseвыполняется после того, tryкак нет исключений. Необязательное условие else выполняется, если и когда управление выходит из конца предложения try. 2 Исключения в предложении else не обрабатываются предыдущим, за исключением предложений.

У погружения в python есть пример, где, если я правильно понимаю, в tryблоке они пытаются импортировать модуль, когда это не удается, вы получаете исключение и связываете значение по умолчанию, но когда это работает, у вас есть возможность перейти вelse блок и связать то, что требуется (см. ссылка на пример и пояснения).

Если вы попытаетесь выполнить работу в catchблоке, это может вызвать еще одно исключение - я думаю, именно здесь elseблок пригодится.


4
«Исключения в предложении else не обрабатываются предыдущим, кроме предложений». Это полезная часть. Спасибо.
geowa4

«Необязательное условие else выполняется, если и когда управление выходит за пределы конца предложения try» - это еще одно отличие, поскольку вы можете вернуться из tryблока.
Томер W

4

Вот и все. Блок «else» предложения try-exc существует для кода, который выполняется, когда (и только когда) успешная операция выполняется. Его можно использовать и злоупотреблять.

try:
    fp= open("configuration_file", "rb")
except EnvironmentError:
    confdata= '' # it's ok if the file can't be opened
else:
    confdata= fp.read()
    fp.close()

# your code continues here
# working with (possibly empty) confdata

Лично мне это нравится, и я использую его, когда это уместно. Семантически группирует утверждения.


2

Возможно использование может быть:

#debug = []

def debuglog(text, obj=None):
    " Simple little logger. "
    try:
        debug   # does global exist?
    except NameError:
        pass    # if not, don't even bother displaying
    except:
        print('Unknown cause. Debug debuglog().')
    else:
        # debug does exist.
        # Now test if you want to log this debug message
        # from caller "obj"
        try:
            if obj in debug:
                print(text)     # stdout
        except TypeError:
            print('The global "debug" flag should be an iterable.')
        except:
            print('Unknown cause. Debug debuglog().')

def myfunc():
    debuglog('Made it to myfunc()', myfunc)

debug = [myfunc,]
myfunc()

Может быть, это приведет вас к пользе.


2

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

#in a long running loop
try:
    query = queue.get()
    conn = connect_to_db(<main db>)
    curs = conn.cursor()
    try:
        curs.execute("<some query on user input that may fail even if sanitized">)
    except DBError:
        logconn = connect_to_db(<logging db>)
        logcurs = logconn.cursor()
        logcurs.execute("<update in DB log with record of failed query")
        logcurs.close()
        logconn.close()
    else:

        #we can't put this in main try block because an error connecting
        #to the logging DB would be indistinguishable from an error in 
        #the mainquery 

        #We can't put this after the whole try: except: finally: block
        #because then we don't know if the query was successful or not

        logconn = connect_to_db(<logging db>)
        logcurs = logconn.cursor()
        logcurs.execute("<update in DB log with record of successful query")
        logcurs.close()
        logconn.close()
        #do something in response to successful query
except DBError:
    #This DBError is because of a problem with the logging database, but 
    #we can't let that crash the whole thread over what might be a
    #temporary network glitch
finally:
    curs.close()
    conn.close()
    #other cleanup if necessary like telling the queue the task is finished

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


1

elseБлок часто может существовать в дополнение к функциональности , которая происходит в каждом exceptблоке.

try:
    test_consistency(valuable_data)
except Except1:
    inconsistency_type = 1
except Except2:
    inconsistency_type = 2
except:
    # Something else is wrong
    raise
else:
    inconsistency_type = 0

"""
Process each individual inconsistency down here instead of
inside the except blocks. Use 0 to mean no inconsistency.
"""

В этом случае inconsistency_typeустанавливается в каждом блоке кроме, так что поведение дополняется в случае отсутствия ошибок вelse .

Конечно, я описываю это как шаблон, который может когда-нибудь появиться в вашем собственном коде. В этом конкретном случае вы просто устанавливаете inconsistency_type0 перед tryблоком в любом случае.


1

Вот еще одно место, где мне нравится использовать этот шаблон:

 while data in items:
     try
        data = json.loads(data)
     except ValueError as e:
        log error
     else:
        # work on the `data`

1
Вы можете просто использовать continueвзамен - паттерн «пробейся раньше». Это позволяет вам отказаться от предложения else и отступа, что делает код более легким для чтения.
Мальта

1

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

while True:
    try:
        r = random.random()
        some_operation_that_fails_for_specific_r(r)
    except Exception:
        continue
    else:
        break

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


1
Вы можете сделать это, поместив breakвнутреннюю часть tryв конце, что чище ИМО, и вам не нужно else. Кроме того, continueэто действительно не нужно, вы можете просто pass.
Дирбайо

1

Я нашел elseполезным для работы с возможно неправильным файлом конфигурации:

try:
    value, unit = cfg['lock'].split()
except ValueError:
    msg = 'lock monitoring config must consist of two words separated by white space'
    self.log('warn', msg)
else:
     # get on with lock monitoring if config is ok

Исключение при чтении lockконфигурации отключает мониторинг блокировки, а ValueErrors записывает полезное предупреждающее сообщение.


1

Предположим, что ваша логика программирования зависит от того, есть ли в словаре запись с заданным ключом. Вы можете проверить результат dict.get(key)использования if... else...конструкции, или вы можете сделать:

try:
    val = dic[key]
except KeyError:
    do_some_stuff()
else:
    do_some_stuff_with_val(val)

-1

Я бы добавил еще один вариант использования, который кажется простым при обработке сессий БД:

    # getting a DB connection 
    conn = db.engine.connect()

    # and binding to a DB session
    session = db.get_session(bind=conn)

    try:
        # we build the query to DB
        q = session.query(MyTable).filter(MyTable.col1 == 'query_val')

        # i.e retrieve one row
        data_set = q.one_or_none()

        # return results
        return [{'col1': data_set.col1, 'col2': data_set.col2, ...}]

    except:
        # here we make sure to rollback the transaction, 
        # handy when we update stuff into DB
        session.rollback()
        raise

    else:
        # when no errors then we can commit DB changes
        session.commit()

    finally:
        # and finally we can close the session
        session.close()

-17

else:Блок сбивает с толку , и (почти) бесполезно. Это также является частью forи whileотчетности.

На самом деле, даже в ifзаявлении else:можно по-настоящему ужасно злоупотреблять, создавая ошибки, которые очень трудно найти.

Учти это.

   if a < 10:
       # condition stated explicitly
   elif a > 10 and b < 10:
       # condition confusing but at least explicit
   else:
       # Exactly what is true here?
       # Can be hard to reason out what condition is true

Подумай дважды else:. Это вообще проблема. Избегайте его, за исключением случаев, в if-statement и даже тогда подумайте о документировании elseусловия -, чтобы сделать его явным.


6
Я бы не согласился с этим. В блоке "if-elif" "else" используется как "default", будет использоваться в блоке "case" языка Си. Всегда рекомендуется обрабатывать случай «по умолчанию», даже если вы думаете, что вы охватили все случаи в различных условиях.
Иосип

1
@Josip: использование по умолчанию может сбивать с толку. Проблема состоит в том, чтобы четко определить условие, которое является этим «значением по умолчанию». Плохо определенное условие по умолчанию может быть основной причиной ошибочного поведения. Иначе может быть причиной путаницы. Это должно быть очень тщательно продумано во всех случаях, а не просто пытаться, на время и во время, но если и так.
S.Lott

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

1
@ S.Lott "Это уменьшит багги" - и я считаю, что это неверно. Я думаю, что у нас просто есть подлинная разница во мнениях. Плохие программисты всегда находят способы писать глючные программы. Всегда. Хорошие программисты всегда ищут хорошие практики и могут написать хороший код практически на любом языке. Исключение полезных конструкций просто дает меньше возможностей хорошим программистам, в то же время не особенно помогая плохим, так как они способны изобрести бесконечное количество способов сделать все возможное.
июля

5
Рассмотрим: if x > 0: return "yes"а if x <= 0: return "no". Теперь человек приходит и меняет одно из условий, чтобы сказать, x > 1но забывает изменить другое. Как это уменьшить количество ошибок, которые будут зафиксированы. if elseпункты иногда много разделены. СУХОЙ это хорошая практика, гораздо чаще, чем нет, правда. (Извините за двойной пост).
июля
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.