В python, как повторно запустить функцию, если она выдает ошибку

Ниже приведены фрагменты моих кодов. Я хотел знать, что гипотетически, если функция main2 () по какой-то причине выдает ошибку, как мне заставить мое исключение запустить ту же функцию еще раз, скажем, 3 раза, прежде чем она сломается?

Чтобы добавить сюда, любая из функций может вызвать ошибку (не только main2 ()), также у меня может быть не только 3, но и многие другие функции.

import numpy as np

def main():
    np.load('File.csv')

def main1():
    np.load('File1.csv')

def main2():
    np.load('File2.csv')

for i in range(1, 10):
    try:
        main()
        main2()
        main3()
    except Exception as e:
        print e
    else:
        break

person user13412850    schedule 15.06.2020    source источник


Ответы (5)


Вы можете сделать это с помощью декоратора повторов Python

@retry((Exception), tries=3, delay=0, backoff=0)
def main2():
   np.load('File2.csv')

Это будет работать так же, как если бы вы написали:

error_counter = 0
    def main2():
       try:
          np.load('File2.csv')
       except:
          if error_counter < 3
             error_counter += 1
             main2()
          raise Exception("Will not try again, have tried 3 times")  
       error_counter = 0

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

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

Кроме того, не следует перехватывать все исключения, так как кэшировать все - плохая практика. Подробнее, почему это плохо

person Fredrik    schedule 15.06.2020
comment
спасибо, я только что изменил свой первоначальный вопрос. Это не просто main2 (), которая могла выдать ошибку, это был гипотетический пример. любая из 3 функций может вызвать ошибку, и, скорее всего, у меня будет 6-7 функций. поэтому мне потребуется гибкость, чтобы повторно запустить любую функцию, которая выдает ошибку - person user13412850; 15.06.2020
comment
Вы можете использовать декоратор над каждой функцией, которая в нем нуждается, и указать, какое исключение вы хотите кэшировать. Я включил main2 только в качестве примера, основанного на исходном вопросе. - person Fredrik; 15.06.2020
comment
это, похоже, работает для меня, мне придется еще немного его протестировать, но пока все выглядит хорошо. спасибо Agian. - person user13412850; 15.06.2020

Вот идиома, которую вы можете попробовать:

for _ in range(3):  # try 3 times
    try:
        main2()
        break       # as soon as it works, break out of the loop
    except Exception as e:
        print e
        continue    # otherwise, try again
else:               # if the loop exited normally, e.g. if all 3 attempts failed
    pass
    # do_something...

Обратите внимание на отступ. else здесь прикреплен к for, а не к try.

person Green Cloak Guy    schedule 15.06.2020
comment
спасибо, я думаю, что мне здесь нужно, так это гибкость, чтобы любая из трех функций могла выдать ошибку (а не только main2 ()). и честно говоря, со временем у меня может появиться еще больше функций, так как это будет работать? - person user13412850; 15.06.2020
comment
Есть разные _1 _ / _ 2_ приспособления для каждого из них? Зависит от того, что делают ваши три функции и как они связаны друг с другом. Одна вещь, которую вы могли бы сделать, - превратить это в функцию, в которой вы передаете n количество раз, чтобы попытаться main2 запустить функцию, а затем просто вызвать эту функцию для той функции, которую хотите запустить. Что позволит вам сделать его более лаконичным. - person Green Cloak Guy; 15.06.2020

К сожалению, реализация пользовательского декоратора retry часто может быть немного затруднительной. Если вы хотите настроить их логику или отрегулировать их, на самом деле это может довольно быстро стать довольно сложным. Существует библиотека Python под названием Backoff-Utils, которая поддерживает очень надежные и легко расширяемые стратегии повтора / отката (полное раскрытие: я предвзято, поскольку я являюсь автором этой библиотеки).

В своем гипотетическом вопросе вы можете использовать библиотеку в стратегии на основе декоратора:

from backoff_utils import backoff, apply_backoff, strategies

@apply_backoff(strategies.Fixed, max_tries = 3, catch_exceptions = [type(ValueError)])
def main2():
    # np.load('File2.csv')
    raise ValueError
    print("In main2")

или вы можете использовать его в стратегии, основанной на функциях, при вызове main2():

result = backoff(main2,
                 max_tries = 3,
                 catch_exceptions = [type(ValueError)],
                 strategy = strategies.Fixed)

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

Используя библиотеку, вы можете использовать любое количество других стратегий повтора / задержки, включая экспоненциальный откат, Фибоннаци, линейную прогрессию и полиномиальную. Вы также можете настроить и создать свои собственные стратегии задержки. И вы можете включить собственные обработчики успеха / неудачи и различные альтернативные пути для разных типов ситуаций.

Конечно, вся эта гибкость избыточна для вашего конкретного случая использования - вам не нужно так много. Но это может быть проще, чем беспокоиться о копировании / вставке / обслуживании собственного декоратора повтора, и он дает вам дополнительные встроенные параметры, если вы обнаружите, что в будущем вам понадобятся более сложные стратегии повтора.

Если это будет полезно, вы можете найти довольно подробную документацию здесь: https://backoff-utils.readthedocs.io/en/latest/index.html

Надеюсь это поможет!

person Chris Modzelewski    schedule 11.07.2020

Можешь попробовать

for i in range(1, 10):
    error_max = 3
    error_counter = 0
    try:
        main()
        try:
            main2()
        except Exception as e:
            counter += 1
            if counter == 3:
                raise e
            else:
                continue
        main3()
    except Exception as e:
        print e
    else:
        break

Этот код будет запускать функцию main2() до тех пор, пока он не получит 3 ошибки, а при первых 2 он снова запустит цикл.

person Leo Arad    schedule 15.06.2020

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

def main():
    # np.load('File.csv')
    raise ValueError
    print("In main")

def main1():
    # np.load('File1.csv')
    raise ValueError
    print("In main1")

def main2():
    # np.load('File2.csv')
    raise ValueError
    print("In main2")

for i in range(1, 10):
    try:
        main()
        main2()
        main3()
    except Exception as e:
        print(e)
    else:
        break

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

person MockinJay    schedule 15.06.2020