Сделайте retry(attempts, exceptions), где attempts включает первый вызов. Перехватывайте только указанные исключения, а после последней неудачи пробрасывайте исходное исключение через raise.
Создание декоратора с автоматическим повтором вызова при ошибке
Сделайте retry(attempts, exceptions), где attempts включает первый вызов. Перехватывайте только указанные исключения, а после последней неудачи пробрасывайте исходное исключение через raise.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Требуется написать декоратор, который будет выполнять повторные попытки вызова функции, если она бросает исключение. Ошибкой считается любой выброшенный Exception. Пример использования:
@retry(...)
def func(data: str) -> str:
if random.random() > 0.5:
raise ValueError(data)
return data
Декоратор должен принимать параметры, позволяющие задать количество попыток и типы исключений, которые следует перехватывать.
Ответ
Сделайте retry(attempts, exceptions), где attempts включает первый вызов. Перехватывайте только указанные исключения, а после последней неудачи пробрасывайте исходное исключение через raise.
Вариант для синхронных функций:
from functools import wraps
import time
def retry(attempts=3, exceptions=(Exception,)):
if type(attempts) is not int or attempts < 1:
raise ValueError('attempts must be positive')
def decorate(func):
@wraps(func)
def wrapper(*args, **kwargs):
for index in range(attempts):
try:
return func(*args, **kwargs)
except exceptions:
if index == attempts - 1:
raise
time.sleep(0.1 * 2 ** index)
return wrapper
return decorate
Например, @retry(3, (ValueError,)) делает максимум три вызова. Другие ошибки не перехватываются. Повторы требуют идемпотентности внешних действий; для async вместо блокирующего сна нужен await asyncio.sleep.