Этот код будет застрять в цикле навсегда, пока не возникнет исключение. Это всего лишь простой пример, и МОЖЕТ потребовать от вас преждевременного выхода из цикла или перерыва между попытками.

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

while True:
    try:
        #code with possible error
    except:
         continue
    else:
         #the rest of the code
         break

Это похоже на хак для меня. Есть ли более Pythonic способ сделать это?

 Brad Koch30 мая 2014 г., 20:06
Слово предупреждения: вы должны добавить некоторую степень защиты от постоянных сбоев, таких как режим сна или максимальный предел попыток. В противном случае ваш процесс будет потреблять процессор бесконечно, как заметил @ user9876.
 greatwolf19 янв. 2011 г., 05:41
Вы можете быть заинтересованы в этомпредложение обмена стека, Он почти готов начать бета-версию, нужно еще немного.
 user987605 янв. 2011 г., 18:15
Эээ ... что происходит, когда умирает удаленный сервер? Будет ли это сидеть там, потребляя 100% ядра процессора?
 user39576005 янв. 2011 г., 18:24
@aand: Нет. Если возникает исключение, он хочет попробовать еще раз (читай:continue), но если не возникает никаких исключений, он хочет кое-что (набросал в комментарии) и убрать эту странную злоупотребление циклом. (else выполняется, если не возникает исключение, это недостающий фрагмент?)
 Ankit Jaiswal05 янв. 2011 г., 18:17
продолжить должен быть в другом и сломать в кроме. Это опечатка?

Ответы на вопрос(9)

e = ''
while e == '':
    try:
        response = ur.urlopen('https://https://raw.githubusercontent.com/MrMe42/Joe-Bot-Home-Assistant/mac/Joe.py')
        e = ' '
    except:
        print('Connection refused. Retrying...')
        time.sleep(1)

', и цикл while проверяет, является ли он все еще ''. Если обнаружена ошибка в операторе try, он печатает, что в соединении было отказано, ждет 1 секунду и затем начинается заново. Это будет продолжаться до тех пор, пока не будет ошибки в try, который затем устанавливает e в '', что убивает цикл while.

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

def try_until(func, max_tries, sleep_time):
    for _ in range(0,max_tries):
        try:
            return func()
        except:
            sleep(sleep_time)
    raise WellNamedException()
    #could be 'return sensibleDefaultValue'

Можно потом так назвать

result = try_until(my_function, 100, 1000)

Если вам нужно передать аргументыmy_function, вы можете сделать это, имеяtry_until пересылать аргументы или заключая их в лямбду без аргументов:

result = try_until(lambda : my_function(x,y,z), 100, 1000)
Решение Вопроса

(что было бы более читабельным в любом случае, так как условие дляbreak там сwhile), вы можете создать переменнуюresult = None и покаis None, Вы также должны настроить переменные, и вы можете заменитьcontinue с семантически возможно правильнымpass (вам все равно, если произойдет ошибка, вы просто хотите ее игнорировать) и отбросьтеbreak - это также возвращает остальную часть кода, который выполняется только один раз, из цикла. Также обратите внимание, что голыеexcept: пункты злые по причинамприведено в документации.

Пример, включающий все вышеперечисленное:

result = None
while result is None:
    try:
        # connect
        result = get_data(...)
    except:
         pass
# other code that uses result but is not involved in getting it
 Brad Koch30 мая 2014 г., 20:03
Если существует постоянная причина, по которой соединение не удается, это решение будет устранено бесконечным циклом.
 Brad Koch30 мая 2014 г., 21:03
Но любой предложенный ответ должен быть безопасным, или, по крайней мере, обратите внимание на подводные камни. Это не обеспечивает защиту от 100% использования процессора и ставит под угрозу будущих читателей.
 user39576030 мая 2014 г., 20:50
@BradKoch Конечно. Это присущему вопросу, и, кроме того, любое исправление (например, общее время ожидания или ограниченное количество попыток) относительно ортогонально изменениям, которые я описываю.

itertools.iter_except Рецепты инкапсулируют эту идею «повторного вызова функции до возникновения исключения». Это похоже на принятый ответ, но рецепт дает вместо этого итератор.

Из рецептов:

def iter_except(func, exception, first=None):
    """ Call a function repeatedly until an exception is raised."""
    try:
        if first is not None:
            yield first()            # For database APIs needing an initial cast to db.first()
        while True:
            yield func()
    except exception:
        pass

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

Код

import more_itertools as mit

list(mit.iter_except([0, 1, 2].pop, IndexError))
# [2, 1, 0]

подробности

Здесьpop метод (или заданная функция) вызывается для каждой итерации объекта списка доIndexError Поднялся.

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

mit.iter_except(connect_function, ConnectionError)

На этом этапе обработайте его как любой другой итератор, зацикливая его или вызываяnext().

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

start = 0
str_error = "Not executed yet."
while str_error:
    try:
        # replace line below with your logic , i.e. time out, max attempts
        start = raw_input("enter a number, 0 for fail, last was {0}: ".format(start))
        new_val = 5/int(start)
        str_error=None
    except Exception as str_error:
         pass

ПРЕДУПРЕЖДЕНИЕ: Этот код будет застрять в цикле навсегда, пока не возникнет исключение. Это всего лишь простой пример, и МОЖЕТ потребовать от вас преждевременного выхода из цикла или перерыва между попытками.

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

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

from urllib.error import HTTPError
import traceback
from time import sleep


attempts = 10
while attempts > 0:
    try:
        #code with possible error
    except HTTPError:
        attempts -= 1
        sleep(1)
        continue
    except:
        print(traceback.format_exc())

    #the rest of the code
    break

Кроме того, вам не нужен блок еще. Из-за продолжения в блоке исключений вы пропускаете оставшуюся часть цикла, пока блок попытки не сработает, пока не будет выполнено условие while или не возникнет исключение, отличное от HTTPError.

декоратор на основе? Вы можете передать в качестве аргументов декоратора список исключений, по которым мы хотим повторить попытку и / или количество попыток.

def retry(exceptions=None, tries=None):
    if exceptions:
        exceptions = tuple(exceptions)
    def wrapper(fun):
        def retry_calls(*args, **kwargs):
            if tries:
                for _ in xrange(tries):
                    try:
                        fun(*args, **kwargs)
                    except exceptions:
                        pass
                    else:
                        break
            else:
                while True:
                    try:
                        fun(*args, **kwargs)
                    except exceptions:
                        pass
                    else:
                        break
        return retry_calls
    return wrapper


from random import randint

@retry([NameError, ValueError])
def foo():
    if randint(0, 1):
        raise NameError('FAIL!')
    print 'Success'

@retry([ValueError], 2)
def bar():
    if randint(0, 1):
        raise ValueError('FAIL!')
    print 'Success'

@retry([ValueError], 2)
def baz():
    while True:
        raise ValueError('FAIL!')

foo()
bar()
baz()

конечно, часть try должна быть перемещена в другую функцию, потому что мы используем ее в обоих циклах, но это всего лишь пример;)

 Eldritch Cheese09 янв. 2014 г., 23:25
Немного позднего комментария, но удвоения кода можно было бы избежать, используя «for _ в itertools.repeat (None, times = попытки):« Если попытка равна None, цикл продолжается вечно, но если попытка - это число, он завершается после стольких итераций.

Может быть, что-то вроде этого:

connected = False

while not connected:
    try:
        try_connect()
        connected = True
    except ...:
        pass
 Brad Koch30 мая 2014 г., 21:06
Любой предложенный ответ должен быть безопасным, или, по крайней мере, обратите внимание на подводные камни. Это не обеспечивает защиту от 100% использования процессора и ставит под угрозу будущих читателей.

который сильно терпит неудачу после 4 попыток и ждет 2 секунды между попытками. Измените, как вы хотите получить то, что вы хотите от этого:

from time import sleep

for x in range(0, 4):  # try 4 times
    try:
        # msg.send()
        # put your logic here
        str_error = None
    except Exception as str_error:
        pass

    if str_error:
        sleep(2)  # wait for 2 seconds before trying to fetch the data again
    else:
        break

Вот пример с откатом:

from time import sleep

sleep_time = 2
num_retries = 4
for x in range(0, num_retries):  
    try:
        # put your logic here
        str_error = None
    except Exception as str_error:
        pass

    if str_error:
        sleep(sleep_time)  # wait before trying to fetch the data again
        sleep_time *= 2  # Implement your backoff algorithm here i.e. exponential backoff
    else:
        break
 Ganesh Krishnan18 февр. 2018 г., 23:44
Мне нравится этот ответ лучше, чем другие, потому что этот "приятен" другим процессам из-за функции сна и также имеет ограниченные попытки.

Ваш ответ на вопрос