Как повторить попытку после исключения?
У меня есть цикл, начинающийся с for i in range(0, 100) . Обычно он работает правильно, но иногда он терпит неудачу из-за сетевых условий. В настоящее время я настроен так, что при ошибке он будет continue в предложении except (перейдите к следующему номеру для i ). Возможно ли, чтобы я переназначил тот же номер до i и снова пропустил неудачную итерацию цикла?
Вы можете использовать range(100) без первого параметра. Если вы используете Python 2.x, вы можете даже использовать xrange(100) , это генерирует итератор и использует меньше памяти. (Не то чтобы это имело значение только с 100 объектами.)
есть очень элегантное решение, использующее декораторы с поддержкой обработки произвольных исключений в этом потоке
Python является настолько плохим языком, что он даже не предоставляет аналог On Error . Resume of Basic . Shame.
16 ответов
Сделайте while True внутри цикла for, поместите код try внутри и перерыв в этом цикле while только тогда, когда ваш код преуспеет.
for i in range(0,100): while True: try: # do stuff except SomeSpecificException: continue break
За исключением использования except SomeSpecificError: вместо except SomeSpecificError: всех исключений.
@zneak:»I «Мне было интересно, могу ли я переназначить мне тот же номер и снова пройти через неудачную итерацию цикла».
@Ignacio Игнасио, а ? continue повторит while цикл, конечно, не for , так (!) i не «следующий» ничего — это точно так же , как это было на предыдущей (не удалось) ножку же while , конечно.
Как отмечает xorsyst, желательно установить ограничение на количество попыток. В противном случае вы можете застрять в петле довольно долго.
Я бы предложил удалить цикл for из показанного примера или использовать его вместо while True . На мой взгляд, это не имеет никакой выгодной ценности. Это может смутить читателей (как показано в комментариях выше).
Я предпочитаю ограничивать количество повторений, так что, если есть проблема с этим конкретным элементом, вы, в конце концов, перейдете к следующему:
for i in range(100): for attempt in range(10): try: # do thing except: # perhaps reconnect, etc. else: break else: # we failed all the attempts - deal with the consequences.
Каковы последствия второй else , как относится к управлению потоком? При каких обстоятельствах выполняется часть кода «мы потерпели неудачу . «?
@g33kz0r g33kz0r конструкция for-else в Python выполняет предложение else, если цикл for не прерывается. Таким образом, в этом случае этот раздел выполняется, если мы попробуем все 10 попыток и всегда получим исключение.
Это отличный ответ! Действительно заслуживает гораздо больше голосов. Он прекрасно использует все возможности Python, особенно менее известные else: условие for .
Вам не нужен перерыв в конце попытки: часть? С дополнительным разрывом в try :, если процесс завершается успешно, цикл прерывается, если он не завершается успешно, он сразу переходит к части исключения. Имеет ли это смысл? Если я не ставлю перерыв в конце попытки: он просто делает это 100 раз.
Тристан: Вы хотите, чтобы он делал это 100 раз — еще несколько раз (повторяет попытку), если некоторые из них терпят неудачу. Комментарий в коде, возможно, должен говорить что-то вроде # do thing with each i . Внешний цикл — это то, что вы действительно пытаетесь сделать (итерация действия), а внутренний цикл — для повторения любых сбоев, которые происходят среди этих 100 действий.
Я также предпочитаю петлю для повторной попытки. Недостаток в этом коде заключается в том, что если вы хотите повторно вызвать исключение при отказе от попытки, вам нужно что-то вроде «если попытка = 9: поднять» внутри предложения except , и не забывайте использовать 9, а не 10.
Хороший вопрос @PaulMcGuire. В качестве альтернативы у вас может быть локальная переменная last_exception которую вы назначаете в цикле попытки, а затем просто повышаете ее, если это не None во внешнем остальном.
@PaulMcG Разве не для этого предназначен последний пункт else ? Если вы поставите # do thing в этом предложении, должно возникнуть необработанное исключение (если действие не выполнено).
@xorsyst, как на самом деле восстановить связь с частью except ? Скажем, если вы попытаетесь подключиться к URL-адресу в части try но не смогли из-за некоторых обычных ошибок, в большинстве случаев это временные ошибки, которые можно легко исправить с помощью повторных попыток, вы хотите восстановить соединение с URL-адресом в части кроме, при условии, что все URL во внешнем цикле фактически доступны. Это может быть очень легко исправить, но я действительно новичок в python и очистке данных, не могли бы вы мне помочь?
@JasonGoal, это звучит как более конкретный вопрос, и я не совсем уверен, что вы спрашиваете. Я предлагаю поднять новый вопрос о переполнении стека с помощью некоторого примера кода.
Пакет повторных попыток — это хороший способ повторить блок кода при ошибке.
@retry(wait_random_min=1000, wait_random_max=2000) def wait_random_1_to_2_s(): print("Randomly wait 1 to 2 seconds between retries")
есть ли в любом случае вы можете напечатать номер повторной попытки каждый раз, когда она не удалась?
Как я понял, не поддерживается, более активным форком является github.com/jd/tenacity и, возможно, также можно использовать github.com/litl/backoff .
Вот решение, подобное другим, но оно приведет к возникновению исключения, если ему не удастся выполнить заданное число или повторить попытку.
tries = 3 for i in range(tries): try: do_the_thing() except KeyError as e: if i < tries - 1: # i is zero indexed continue else: raise break
Очень хорошее решение, спасибо. Это можно улучшить, добавив задержку между каждой попыткой. Очень полезно при работе с API.
Более "функциональный" подход без использования этих уродливых циклов while:
def tryAgain(retries=0): if retries > 10: return try: # Do stuff except: retries+=1 tryAgain(retries) tryAgain()
Я извиняюсь, но это кажется намного более уродливым, чем варианты "некрасивых циклов while"; и я увлекаюсь функциональным программированием .
Вы должны убедиться, что вы не выполняете глубокие рекурсии - размер стека по умолчанию в Python - 1000
Самый ясный способ - явно установить i . Например:
Это не делает то, что просил ОП. Это может произойти, если вы поставите i += 1 сразу после # do stuff .
Общее решение с тайм-аутом:
import time def onerror_retry(exception, callback, timeout=2, timedelta=.1): end_time = time.time() + timeout while True: try: yield callback() break except exception: if time.time() > end_time: raise elif timedelta > 0: time.sleep(timedelta)
for retry in onerror_retry(SomeSpecificException, do_stuff): retry()
Можно ли указать отдельную функцию для проверки ошибок? Он должен принять выходные данные обратного вызова и перейти к функции проверки ошибок, чтобы определить, был ли это сбой или успех, вместо использования простого except exception:
Это решение не работает. trinket.io/python/caeead4f6b Исключение, генерируемое do_stuff, не передается генератору. В любом случае, зачем это? do_stuff вызывается в теле цикла for, который находится на внешнем уровне сам по себе, а не в генераторе.
Ваше право, но по другой причине: функция callback никогда не вызывается. Я забыл скобки, заменить на callback() .
for i in range(100): def do(): try: ## Network related scripts except SpecificException as ex: do() do() ## invoke do() whenever required inside this loop
Пожалуйста, имейте в виду, что это не проверка исключений, а возвращаемое значение. Повторяется до тех пор, пока оформленная функция не вернет True.
Немного измененная версия должна сделать свое дело.
Вы можете использовать пакет повторной проверки Python. Retrying
Это написано на Python, чтобы упростить задачу добавления поведения повтора к чему угодно.
Использование while и счетчика:
Если вы хотите найти решение без вложенных циклов и использования break при успехе, вы можете разработать быструю retriable для любой итерации. Вот пример сетевой проблемы, с которой я часто сталкиваюсь - срок сохраненной аутентификации истекает. Использование этого будет читать так:
client = get_client() smart_loop = retriable(list_of_values): for value in smart_loop: try: client.do_something_with(value) except ClientAuthExpired: client = get_client() smart_loop.retry() continue except NetworkTimeout: smart_loop.retry() continue
for _ in range(5): try: # replace this with something that may fail raise ValueError("foo") # replace Exception with a more specific exception except Exception as e: err = e continue # no exception, continue remainder of code else: break # did not break the for loop, therefore all attempts # raised an exception else: raise err
Моя версия похожа на несколько выше, но не использует отдельный во while цикла, и вновь поднимает последнее исключение, если все повторные попытки терпят неудачу. Можно явно установить err = None наверху, но это не является строго необходимым, поскольку он должен выполнять только последний блок else если произошла ошибка и, следовательно, установлен err .
Geospatial Solutions Expert
When using loops in python, there are situations when an external factor may influence the way your program runs. You may want your program to exit a loop completely, skip part of a loop before continuing, or ignore that external factor. You can do these actions with break, continue, and pass statements respectively.
The "pass" statement is a no-operation (meaning that it doesn't do anything). The program just continues to the next statement, which is why you get an error.
The "break" statement exits the loops and continues running from the next statement immediately after the loop. In this case, there are no more statements, which is why your program terminates.
The "continue" statement restarts the loop but with the next item.
Sample Code
for letter in 'Python':
if letter == 'h':
break # continue pass
print ('Current Letter :', letter)
Testing the above code for: break, continue and pass will yield different results as seen below:-
If we introduce a "Try . Except" block and intentionally call an error, only pass will print the error statement at the end of the block statement.