예외 후에 다시 시도하는 방법?


252

로 시작하는 루프가 for i in range(0, 100)있습니다. 일반적으로 올바르게 실행되지만 네트워크 상태로 인해 실패하는 경우가 있습니다. 현재 나는 실패 continue했을 때 except 절 에 있도록 설정했습니다 (에 대한 다음 숫자로 계속 i).

동일한 번호를 다시 할당 i하고 실패한 루프 반복을 다시 실행할 수 있습니까?


1
range(100)첫 번째 매개 변수없이 사용할 수 있습니다 . Python 2.x를 사용하면조차 사용할 수 있습니다 xrange(100). 이것은 반복자를 생성하고 더 적은 메모리를 사용합니다. (100 개체에만 중요하지는 않습니다.)
Georg Schölly


2
스레드에서 임의의 예외 처리를 지원하는 데코레이터를 사용하는 매우 우아한 솔루션이 있습니다.
zitroneneis

답변:


378

를 수행 while True루프를 들어, 넣어 당신의 내부에 try그에서 코드 내부 및 휴식 while코드가 성공에만 루프.

for i in range(0,100):
    while True:
        try:
            # do stuff
        except SomeSpecificException:
            continue
        break

30
@Ignacio, ? continue시도 while루프는 물론, 하지for, 너무 (!) i되어 있지 "다음"아무것도 - 그것은 같은 이전 (실패) 다리에 있던 정확히 동일합니다 while물론,.
Alex Martelli

13
xorsyst가 지적했듯이 재시도 제한을 두는 것이 좋습니다. 그렇지 않으면 꽤 오랫동안 루핑 될 수 있습니다.
Brad Koch

2
이것은 훌륭한 예입니다 : medium.com/@echohack/…
Tony Melony

7
나는 while : line을 분명히 생략합니다. 그렇지 않으면 브레이크가 외부 루프를 계속 소모합니다.
Jan

1
@ Sankalp,이 답변은 질문 텍스트에 적합한 것으로 보입니다.
zneak

188

재시도 횟수를 제한하여 특정 항목에 문제가있는 경우 다음 항목으로 계속 진행할 수 있습니다.

for i in range(100):
  for attempt in range(10):
    try:
      # do thing
    except:
      # perhaps reconnect, etc.
    else:
      break
  else:
    # we failed all the attempts - deal with the consequences.

3
@ g33kz0r for 루프가 중단되지 않으면 파이썬의 for-else 구문은 else 절을 ​​실행합니다. 따라서이 경우 10 번의 시도를 모두 시도하고 항상 예외가 발생하면 해당 섹션이 실행됩니다.
xorsyst

7
이것은 좋은 답변입니다! 훨씬 더 많은 공감대가 필요합니다. 파이썬의 모든 기능, 특히 덜 알려진 else:절을 완벽하게 사용합니다 for.
pepoluan

2
시도가 끝날 때 휴식을 취할 필요가 없습니다 : 부분? try :에서 추가 break를 사용하면 프로세스가 성공적으로 완료되면 루프가 중단되고 성공적으로 완료되지 않으면 예외 부분으로 바로 이동합니다. 말이 돼? 시도가 끝날 때 휴식을 취하지 않으면 100 번 일을합니다.
Tristan

1
@Tristan-의 else절은 try당신이 찾고있는 "성공하면 깨짐"을합니다.
PaulMcG

1
또한 재 시도를위한 for 루프를 선호합니다. 이 코드의 주름은 시도를 포기할 때 예외를 다시 발생 시키려면 except절 안에 "if 시도 = 9 : 발생"과 같은 것이 필요하고 10이 아닌 9를 사용해야
한다는 점입니다

69

다시 시도 패키지는 실패 코드 블록을 재 시도 할 수있는 좋은 방법입니다.

예를 들면 다음과 같습니다.

@retry(wait_random_min=1000, wait_random_max=2000)
def wait_random_1_to_2_s():
    print("Randomly wait 1 to 2 seconds between retries")

4
더 일반적으로, pypi에는 재시도 데코레이터를위한 여러 패키지가 있습니다 : pypi.python.org/…
kert

어쨌든 실패 할 때마다 재시도 횟수를 인쇄 할 수 있습니까?
dim_user

8
내가 이해하지 않았으므로 더 활발한 포크는 github.com/jd/tenacity 이며 github.com/litl/backoff도 사용할 수 있습니다.
Alexey Shrub 2012 년

22

여기에 다른 솔루션과 유사한 솔루션이 있지만 규정 된 수 또는 재 시도에서 성공하지 못하면 예외가 발생합니다.

tries = 3
for i in range(tries):
    try:
        do_the_thing()
    except KeyError as e:
        if i < tries - 1: # i is zero indexed
            continue
        else:
            raise
    break

좋은 대답이지만 변수 이름 retries이 잘못되었습니다. 오히려되어야 tries합니다.
루카스

사실 @Lukas. 결정된.
TheHerk

아주 좋은 해결책 감사합니다. 각 시도 사이에 지연을 추가하여 향상시킬 수 있습니다. API를 다룰 때 매우 유용합니다.
Sam

14

추악한 while 루프를 사용하지 않는보다 "기능적인"접근 방식 :

def tryAgain(retries=0):
    if retries > 10: return
    try:
        # Do stuff
    except:
        retries+=1
        tryAgain(retries)

tryAgain()

13
죄송하지만 "ugly while loops"변형보다 훨씬 더 나빠 보입니다. 그리고 나는 기능적인 프로그래밍을 좋아한다 ...
lvella

9
파이썬의 기본 스택 크기는 1000입니다
Cal Paterson

5
이것이 '기능적'이 되려면 재귀는 다음과 같아야합니다.except: tryAgain(retries+1)
quamrana

이 문제는 변수로 오류를 전달해야한다는 것입니다.
lowzhao

11

가장 명확한 방법은 명시 적으로 설정하는 것 i입니다. 예를 들면 다음과 같습니다.

i = 0
while i < 100:
    i += 1
    try:
        # do stuff

    except MyException:
        continue

37
C 또는 C ++입니까? 말할 수 없습니다.
Georg Schölly

5
@Georg 그것은 질문에 명시된 바와 같이 파이썬입니다. 아니면 어떤 이유로 비꼬는 곳입니까?
Jakob Borg

2
이것은 OP가 요청한 것을 수행하지 않습니다. 당신이 i += 1직후에 넣을 수도 있습니다 # do stuff.
fmalina

5
pythonic이 아닙니다. range이런 종류의 물건에 사용해야합니다 .
Mystic

2
나는 이것이 분명히 범위를 사용해야한다는 것에 동의합니다.
user2662833

5

시간 초과가있는 일반적인 솔루션 :

import time

def onerror_retry(exception, callback, timeout=2, timedelta=.1):
    end_time = time.time() + timeout
    while True:
        try:
            yield callback()
            break
        except exception:
            if time.time() > end_time:
                raise
            elif timedelta > 0:
                time.sleep(timedelta)

용법:

for retry in onerror_retry(SomeSpecificException, do_stuff):
    retry()

오류 점검을 위해 별도의 기능을 지정할 수 있습니까? 콜백의 출력을 가져 와서 오류 검사 기능으로 전달하여 간단한 것을 사용하는 대신 실패 또는 성공 여부를 결정합니다.except exception:
Pratik Khadloya

대신 문을 try … except사용할 수 있습니다 if. 그러나 그것은 덜 파이썬입니다.
Laurent LAPORTE

이 솔루션은 작동하지 않습니다. trinket.io/python/caeead4f6b do_stuff에 의해 발생 된 예외는 생성기로 버블 링되지 않습니다. 어쨌든 왜 그런가요? do_stuff는 for 루프의 본문에서 호출되며 생성기에는 중첩되지 않고 자체적으로 외부 레벨에 있습니다.
isarandi

당신의 권리, 그러나 다른 이유로 : callback함수는 결코 호출되지 않습니다. 괄호를 잊어 버렸습니다 callback().
Laurent LAPORTE

5
for _ in range(5):
    try:
        # replace this with something that may fail
        raise ValueError("foo")

    # replace Exception with a more specific exception
    except Exception as e:
        err = e
        continue

    # no exception, continue remainder of code
    else:
        break

# did not break the for loop, therefore all attempts
# raised an exception
else:
    raise err

내 버전은 위의 몇 가지와 비슷하지만 별도의 while루프를 사용하지 않으며 모든 재 시도에 실패하면 최신 예외를 다시 발생시킵니다. 명시 적으로 err = None상단에 설정할 수 는 있지만 else오류가 발생 하여 최종 블록을 설정 해야하기 때문에 반드시 최종 블록을 실행해야하므로 엄격하게 필요하지는 않습니다 err.



4

재귀 사용

for i in range(100):
    def do():
        try:
            ## Network related scripts
        except SpecificException as ex:
            do()
    do() ## invoke do() whenever required inside this loop

1
출구 조건? 아니면 100 * 무한대로 실행됩니까?
ingyhere

3

while과 카운터 사용하기 :

count = 1
while count <= 3:  # try 3 times
    try:
        # do_the_logic()
        break
    except SomeSpecificException as e:
        # If trying 3rd time and still error?? 
        # Just throw the error- we don't have anything to hide :)
        if count == 3:
            raise
        count += 1

3

파이썬 재시도 패키지를 사용할 수 있습니다. 재시도

그것은 거의 모든 것에 재시도 동작을 추가하는 작업을 단순화하기 위해 파이썬으로 작성되었습니다.


2

retrying: tenacitybackoff(2020 업데이트)에 대한 대안

재 시도 라이브러리는 이전에가는 방법,하지만 슬프게도 그것은 몇 가지 버그를 가지고 있으며, 2016 년 다른 대안이 될 것으로 보인다 이후이 업데이트이 없어 백 오프끈기 . 이 글을 쓰는 동안 강인성은 더 많은 GItHub 별 (2.3k 대 1.2k)을 가지고 있으며 더 최근에 업데이트되었으므로 사용하기로 결정했습니다. 예를 들면 다음과 같습니다.

from functools import partial
import random # producing random errors for this example

from tenacity import retry, stop_after_delay, wait_fixed, retry_if_exception_type

# Custom error type for this example
class CommunicationError(Exception):
    pass

# Define shorthand decorator for the used settings.
retry_on_communication_error = partial(
    retry,
    stop=stop_after_delay(10),  # max. 10 seconds wait.
    wait=wait_fixed(0.4),  # wait 400ms 
    retry=retry_if_exception_type(CommunicationError),
)()


@retry_on_communication_error
def do_something_unreliable(i):
    if random.randint(1, 5) == 3:
        print('Run#', i, 'Error occured. Retrying.')
        raise CommunicationError()

위의 코드는 다음과 같이 출력됩니다.

Run# 3 Error occured. Retrying.
Run# 5 Error occured. Retrying.
Run# 6 Error occured. Retrying.
Run# 6 Error occured. Retrying.
Run# 10 Error occured. Retrying.
.
.
.

에 대한 자세한 설정 tenacity.retrytenacity GitHub 페이지에 나와 있습니다.


1

중첩 루프가없는 솔루션을 원하고 break성공을 불러 retriable일으키는 경우 반복 가능한 빠른 랩핑 을 개발할 수 있습니다. 다음은 자주 실행되는 네트워킹 문제의 예입니다. 저장된 인증이 만료됩니다. 그것을 사용하면 다음과 같이 읽습니다.

client = get_client()
smart_loop = retriable(list_of_values):

for value in smart_loop:
    try:
        client.do_something_with(value)
    except ClientAuthExpired:
        client = get_client()
        smart_loop.retry()
        continue
    except NetworkTimeout:
        smart_loop.retry()
        continue

1

내 코드에서 다음을 사용합니다.

   for i in range(0, 10):
    try:
        #things I need to do
    except ValueError:
        print("Try #{} failed with ValueError: Sleeping for 2 secs before next try:".format(i))
        time.sleep(2)
        continue
    break

0

attempts = 3
while attempts:
  try:
     ...
     ...
     <status ok>
     break
  except:
    attempts -=1
else: # executed only break was not  raised
   <status failed>


0

다음은이 문제에 대한 설명입니다. 다음 retry기능은 다음 기능을 지원합니다.

  • 성공한 경우 호출 된 함수의 값을 리턴합니다.
  • 시도가 소진 된 경우 호출 된 함수의 예외를 발생시킵니다.
  • 시도 횟수 제한 (무제한 0)
  • 시도 간 대기 (선형 또는 지수)
  • 예외가 특정 예외 유형의 인스턴스 인 경우에만 재 시도하십시오.
  • 시도의 선택적 로깅
import time

def retry(func, ex_type=Exception, limit=0, wait_ms=100, wait_increase_ratio=2, logger=None):
    attempt = 1
    while True:
        try:
            return func()
        except Exception as ex:
            if not isinstance(ex, ex_type):
                raise ex
            if 0 < limit <= attempt:
                if logger:
                    logger.warning("no more attempts")
                raise ex

            if logger:
                logger.error("failed execution attempt #%d", attempt, exc_info=ex)

            attempt += 1
            if logger:
                logger.info("waiting %d ms before attempt #%d", wait_ms, attempt)
            time.sleep(wait_ms / 1000)
            wait_ms *= wait_increase_ratio

용법:

def fail_randomly():
    y = random.randint(0, 10)
    if y < 10:
        y = 0
    return x / y


logger = logging.getLogger()
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler(stream=sys.stdout))

logger.info("starting")
result = retry.retry(fail_randomly, ex_type=ZeroDivisionError, limit=20, logger=logger)
logger.info("result is: %s", result)

자세한 내용은 내 게시물 을 참조하십시오.


-2

이 문제를 해결하는 방법에 대한 내 아이디어는 다음과 같습니다.

j = 19
def calc(y):
    global j
    try:
        j = j + 8 - y
        x = int(y/j)   # this will eventually raise DIV/0 when j=0
        print("i = ", str(y), " j = ", str(j), " x = ", str(x))
    except:
        j = j + 1   # when the exception happens, increment "j" and retry
        calc(y)
for i in range(50):
    calc(i)

7
이것은 기본입니다.
Chris Johnson

-2

나는 최근 에이 문제에 대한 해결책으로 파이썬과 함께 일했으며 스택 오버 플로우 방문자와 공유하게되어 기쁘다면 피드백을 보내주십시오.

print("\nmonthly salary per day and year converter".title())
print('==' * 25)


def income_counter(day, salary, month):
    global result2, result, is_ready, result3
    result = salary / month
    result2 = result * day
    result3 = salary * 12
    is_ready = True
    return result, result2, result3, is_ready


i = 0
for i in range(5):
    try:
        month = int(input("\ntotal days of the current month: "))
        salary = int(input("total salary per month: "))
        day = int(input("Total Days to calculate> "))
        income_counter(day=day, salary=salary, month=month)
        if is_ready:
            print(f'Your Salary per one day is: {round(result)}')
            print(f'your income in {day} days will be: {round(result2)}')
            print(f'your total income in one year will be: {round(result3)}')
            break
        else:
            continue
    except ZeroDivisionError:
        is_ready = False
        i += 1
        print("a month does'nt have 0 days, please try again")
        print(f'total chances left: {5 - i}')
    except ValueError:
        is_ready = False
        i += 1
        print("Invalid value, please type a number")
        print(f'total chances left: {5 - i}')

-8

try 절이 성공할 때만 루프 변수를 증가시킵니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.