Для чего предназначено необязательное else
предложение try
оператора?
Для чего предназначено необязательное else
предложение try
оператора?
Ответы:
Операторы в 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
Позволяет убедиться ,
finally
блоком, иIOError
s, которые это поднимает, не пойманы здесьreturn
, continue
или break
.
Есть одна большая причина для использования 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».
Одно использование: протестировать некоторый код, который должен вызвать исключение.
try:
this_should_raise_TypeError()
except TypeError:
pass
except:
assert False, "Raised the wrong exception type"
else:
assert False, "Didn't raise any exception"
(Этот код должен быть абстрагирован в более общий тест на практике.)
Python попробуй еще
Для чего предназначено необязательное
else
предложение оператора try?
Предполагаемое использование состоит в том, чтобы иметь контекст для запуска большего количества кода, если не было исключений, где он должен был быть обработан.
Этот контекст позволяет избежать случайной обработки ошибок, которые вы не ожидали.
Но важно понять точные условия , которые вызывают еще пункт о перспективе, потому что return
, continue
и break
может прервать поток управления в else
.
else
Заявление работает , если нет ни одного исключения , и если не прерывается return
, continue
или break
заявление.
Дополнительный
else
пункт выполняется , если и когда управление оттекает в конце этогоtry
пункта. *
(Добавлен жирный шрифт.) И сноска гласит:
* В настоящее время, управление «оттекает конца» , за исключением того, в случае исключения или исполнения
return
,continue
илиbreak
заявления.
Требуется хотя бы одно предшествующее, кроме предложения ( см. Грамматику ). Так что на самом деле это не «попробуй-иначе», это «попробуй-кроме-еще (-наконец)», с else
(иfinally
) является необязательным.
В 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!
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, которая дала бы мне знать, проглатывается. Хуже того, что если бы я написал это правильно, но метод очистки иногда передавал пользовательский тип, имеющий атрибут с неправильным именем, что приводило к молчаливому сбою на полпути и оставлению файла незамеченным? Удачи в отладке этого.
Я считаю, что это действительно полезно, когда нужно выполнить очистку, даже если есть исключение:
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()
Даже если вы не можете думать об использовании этого прямо сейчас, вы можете поспорить, что должно быть использование для этого. Вот невообразимый образец:
С 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
блока, но тогда это требует некоторого беспорядочного обнаружения, если переменная определена.
something = a[2]; print something
внутри блока try: block?
Там хороший пример try-else
в PEP 380 . По сути, все сводится к разной обработке исключений в разных частях алгоритма.
Это что-то вроде этого:
try:
do_init_stuff()
except:
handle_init_suff_execption()
else:
try:
do_middle_stuff()
except:
handle_middle_stuff_exception()
Это позволяет вам писать код обработки исключений ближе к месту возникновения исключения.
Из ошибок и исключений # Обработка исключений - 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 ... кроме.
Глядя на ссылку Python, кажется, что else
выполняется после того, try
как нет исключений. Необязательное условие else выполняется, если и когда управление выходит из конца предложения try. 2 Исключения в предложении else не обрабатываются предыдущим, за исключением предложений.
У погружения в python есть пример, где, если я правильно понимаю, в try
блоке они пытаются импортировать модуль, когда это не удается, вы получаете исключение и связываете значение по умолчанию, но когда это работает, у вас есть возможность перейти вelse
блок и связать то, что требуется (см. ссылка на пример и пояснения).
Если вы попытаетесь выполнить работу в catch
блоке, это может вызвать еще одно исключение - я думаю, именно здесь else
блок пригодится.
try
блока.
Вот и все. Блок «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
Лично мне это нравится, и я использую его, когда это уместно. Семантически группирует утверждения.
Возможно использование может быть:
#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()
Может быть, это приведет вас к пользе.
Я нашел эту 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
Конечно, если вы можете различить возможные исключения, которые могут быть выброшены, вам не обязательно использовать это, но если код, реагирующий на успешный фрагмент кода, может выдать то же исключение, что и успешный фрагмент, и вы не можете просто отпустите второе возможное исключение или сразу же вернитесь в случае успеха (в моем случае это приведет к прерыванию потока), тогда это пригодится.
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_type
0 перед try
блоком в любом случае.
Вот еще одно место, где мне нравится использовать этот шаблон:
while data in items:
try
data = json.loads(data)
except ValueError as e:
log error
else:
# work on the `data`
continue
взамен - паттерн «пробейся раньше». Это позволяет вам отказаться от предложения else и отступа, что делает код более легким для чтения.
Один из сценариев использования, о котором я могу подумать, - это непредсказуемые исключения, которые можно обойти, если вы попытаетесь снова. Например, когда операции в блоке try содержат случайные числа:
while True:
try:
r = random.random()
some_operation_that_fails_for_specific_r(r)
except Exception:
continue
else:
break
Но если исключение может быть спрогнозировано, вы всегда должны заранее выбирать проверку вместо исключения. Однако не все может быть предсказано, поэтому этот шаблон кода имеет свое место.
break
внутреннюю часть try
в конце, что чище ИМО, и вам не нужно else
. Кроме того, continue
это действительно не нужно, вы можете просто pass
.
Я нашел 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 записывает полезное предупреждающее сообщение.
Предположим, что ваша логика программирования зависит от того, есть ли в словаре запись с заданным ключом. Вы можете проверить результат dict.get(key)
использования if... else...
конструкции, или вы можете сделать:
try:
val = dic[key]
except KeyError:
do_some_stuff()
else:
do_some_stuff_with_val(val)
Я бы добавил еще один вариант использования, который кажется простым при обработке сессий БД:
# 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()
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
условия -, чтобы сделать его явным.
if x > 0: return "yes"
а if x <= 0: return "no"
. Теперь человек приходит и меняет одно из условий, чтобы сказать, x > 1
но забывает изменить другое. Как это уменьшить количество ошибок, которые будут зафиксированы. if else
пункты иногда много разделены. СУХОЙ это хорошая практика, гораздо чаще, чем нет, правда. (Извините за двойной пост).