Для чого передбачається використання необов'язкового else
пункту try
твердження?
Для чого передбачається використання необов'язкового else
пункту try
твердження?
Відповіді:
Виписки в else
блоці виконуються, якщо виконання випадає з нижньої частини поля try
- якщо не було винятком. Чесно кажучи, я ніколи не знаходив потреби.
Однак обробка виключень зазначає:
Використовувати інший пункт краще, ніж додавати додатковий код до пункту спробу, тому що це дозволяє уникнути випадкового лову винятку, який не був піднятий захищеним кодом спробу ... крім оператора.
Отже, якщо у вас є метод, який може, наприклад, кинути an 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
які вирощування тут не спіймані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 try-else
Для чого передбачається використання необов'язкового
else
застереження спробувати test?
Передбачуване використання має мати контекст для запуску більшої кількості коду, якщо не було винятків, коли це очікувалося для обробки.
Цей контекст дозволяє уникнути випадкових помилок, яких ви не очікували.
Але важливо зрозуміти точні умови , які викликають ще пункт про перспективу, тому що return
, continue
і break
може перервати потік управління в else
.
else
Заява працює , якщо немає жодного винятку , і якщо не переривається return
, continue
або break
заяву.
Додатковий
else
пункт виконується , якщо і коли управління відтікає в кінці цьогоtry
пункту. *
(Сміливий додав.) А виноска йде:
* В даний час, управління «відтікає кінця» , за винятком того, в разі виключення або виконання
return
,continue
абоbreak
заяви.
Для цього потрібен принаймні один попередній, крім пункту ( див. Граматику ). Тож насправді це не "пробувати інше", це "пробувати, окрім іншого ((остаточно)", при цьому else
(і finally
) є необов'язковим.
В Python Tutorial конкретизує передбачуваного використання:
Оператор 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
блокомЯкщо ви обробляєте помилку, 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!
Спробуйте, крім іншого, відмінно підходить для поєднання схеми EAFP з набором тексту :
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
застереження, яке, за наявності, повинно відповідати всім, окрім пунктів Це корисно для коду, який повинен бути виконаний, якщо пробний пункт не викликає винятку. Наприклад: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()
Використовувати інший пункт краще, ніж додавати додатковий код до пункту спробу, тому що це дозволяє уникнути випадкового лову винятку, який не був піднятий захищеним кодом спробу ... крім оператора.
Переглядаючи посилання Python, здається, що else
воно виконується після того, try
як немає винятку. Необов'язковий інший пункт виконується, якщо і коли управління відтікає від кінця спробу. 2 Винятки з іншого пункту не обробляються попередніми, окрім пунктів.
Занурення в python має приклад, коли, якщо я правильно розумію, у try
блоці вони намагаються імпортувати модуль, коли це не вдається, ви отримуєте виняток і прив'язуєте за замовчуванням, але коли він працює, у вас є можливість перейти до else
блоку та зв’язати те, що потрібно (див. посилання для прикладу та пояснення).
Якщо ви спробували виконати роботу в catch
блоці, це може спричинити ще один виняток - я думаю, саме тут цей else
блок стане у нагоді.
try
блоку.
Це воно. Блок 'else' пункту спробу виключення існує для коду, який запускається, коли (і лише тоді), коли випробувана операція успішна. Це можна використовувати, і ним можна зловживати.
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" та його відступ, що полегшує читання коду.
Один із сценаріїв використання, який я можу придумати, - це непередбачувані винятки, які можна обійти, якщо спробувати ще раз. Наприклад, коли операції в блоці спробу включають випадкові числа:
while True:
try:
r = random.random()
some_operation_that_fails_for_specific_r(r)
except Exception:
continue
else:
break
Але якщо виняток можна передбачити, завжди слід вибирати перевірку заздалегідь над винятком. Однак не все можна передбачити, тому цей шаблон коду має своє місце.
break
внутрішню try
сторону в кінці, яка чистіша IMO, і вона вам не потрібна 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
заяви, і навіть тоді розглядайте питання документування else
умови - щоб зробити це явним.
if x > 0: return "yes"
і if x <= 0: return "no"
. Зараз людина приходить і змінює одну з умов сказати, x > 1
але забуває змінити іншу. Як це зменшити кількість помилок, які будуть вчинені. if else
Застереження іноді багато рядків один від одного. DRY - це хороша практика, набагато частіше, ніж ні, насправді. (вибачте за подвійний пост).