строку В Python попробуйте до тех пор, пока не произойдет ошибка



title python 3 (8)

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

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

Мне это кажется взломанным. Есть ли более питонический способ сделать это?


Answer #1

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

connected = False

while not connected:
    try:
        try_connect()
        connected = True
    except ...:
        pass

Answer #2

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

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 аргументы или обернув его без аргумента lambda:

result = try_until(lambda : my_function(x,y,z), 100, 1000)

Answer #3

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

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» должна быть перенесена на другую функцию, потому что мы используем ее в обоих циклах, но это просто пример;)


Answer #4

Вот один из них, который тяжело терпит неудачу после 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

Answer #5
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)

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


Answer #6

Рецепты 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() .


Answer #7

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

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

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

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


Answer #8

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

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

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




error-handling