Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 1

Это не станет намного чище. Это не очень чистая вещь. В лучшем случае (что в любом случае было бы более читабельным, поскольку условие 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

Ответ 2

Возможно, что-то вроде этого:

connected = False

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

Ответ 3

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

Ответ 4

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

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)

Ответ 5

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

Ответ 6

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

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

Ответ 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

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

Ответ 8

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 секунду и затем начинается заново. Это будет продолжаться до тех пор, пока не будет ошибки в try, который затем устанавливает e в '', что убивает цикл while.

Ответ 9

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

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

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

Ответ 10

При повторной попытке из-за ошибки вы всегда должны:

  • реализовать предел повторных попыток, или вы можете заблокировать бесконечный цикл
  • реализовать задержку, или вы будете слишком сильно забивать ресурсы, такие как ваш ЦП или и без того проблемный удаленный сервер

Простой общий способ решения этой проблемы при рассмотрении этих проблем заключается в использовании библиотеки отката. Основной пример:

import backoff

@backoff.on_exception(
    backoff.expo,
    MyException,
    max_tries=5
)
def make_request(self, data):
    # do the request

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

Ответ 11

как насчет повторной библиотеки на pypi? Я использую его некоторое время, и он делает именно то, что мне нужно, и даже больше (повторите при ошибке, повторите, когда нет, повторите с таймаутом). Ниже приведен пример с их сайта:

import random
from retrying import retry

@retry
def do_something_unreliable():
    if random.randint(0, 10) > 1:
        raise IOError("Broken sauce, everything is hosed!!!111one")
    else:
        return "Awesome sauce!"

print do_something_unreliable()