반환 코드 및 출력을 기반으로하는 Python 재시도 논리

Sep 09 2020

주어진 재시 도와 지연 횟수로 주어진 명령을 실행하는 함수를 작성하려고합니다. 리턴 코드가 0이고 출력 문자열에 expected_output (partial)이 있으면 루프를 중단하고 리턴 코드와 출력을 리턴해야합니다. 이 코드를 작성하는 것이 더 좋은 것이 있습니까?

cmd check_output_and_retry(cmd, expected_output, delay=1, retry=3):
    for i in range(retry):
        # Run the given command.
        # Return value will be return code with cmd output.
        ret, output = execute_cmd(cmd)
        if ret == 0 and expected_out in output:
            break
        else:
            time.sleep(delay)
    return ret, output

답변

2 scnerd Sep 09 2020 at 00:20

코드 골프의 "이 함수를 작성할 수있는 가장 짧은 방법"을 사용하지 않고 특정 구현에 대한 몇 가지 대안에 주목할 것입니다. 더 간단하다고 생각하는지 고려할 수 있습니다.

  1. if ret == 0와 동일합니다 if not ret. if문 에서 더 읽기 쉽지는 않지만 이 변수를 예를 들어 program_failed = not ret.

  2. expected_out in output출력이 원하는 것인지 평가하기 위해 누군가가 할 수있는 한 가지 일뿐입니다 (예 : 대신 정규식 검사를 원할 수도 있음). 이것을 완전 자유형 함수로 바꾸고 나서 그 함수를 호출하고 그 결과를 사용하고 싶을 수도 있습니다. 예 :if ret == 0 and output_ok(output):

  3. 이 두 가지를 기반으로 조건을 지정하는 것이 더 예쁘다.

    status_ok = ret == 0
    output_ok = check_output(output)
    if status_ok and output_ok:  # ...
    
  4. if진술 의 특성상 else조건이 불필요합니다. 다음과 같이 작성하는 것과 같습니다.

    if condition:
        break
    
    time.sleep(delay)
    
  5. 당신은 return ret, output여부를 프로그램이 혼동 될 수있는 성공합니다. 함수의 목적은 프로그램의 출력을 확인하고 실패하면 재 시도하는 것이므로 프로그램이 재 시도가 부족해서 검사에 실패하면 "성공적으로"반환하는 것이 이상하게 보입니다. 대신 예외를 발생시키는 것을 고려하십시오.

    for _ in range(retry):
        # ...
        if condition:
            break
        # ...
    else:
        raise ValueError("Failed to run program successfully, <some useful information about ret, output, etc.>")
    return output  # Only output, since ``ret`` is guaranteed to be 0
    
  6. Pycharm 및 잠재적으로 다른 Python 스타일러는 사용하지 않는 이름이 지정된 변수를 사용하여 화를 낼 수 있습니다 i. 다음으로 교체하는 것이 좋습니다 _.

    for _ in range(retry):  # ...
    
  7. cmd check_output...파이썬이 def.를 사용하기 때문에 나는 단지 오타 라고 가정 할 것 입니다 .

  8. 독 스트링을 작성하세요!

다시 말하지만, 위의 모든 것이 개선 된 것이 아니라 단지 대안이라고 생각할 수 있습니다. 모두 따라 갔다면 다음과 같은 결과가 나올 수 있습니다.

def check_output_and_retry(cmd, check_output: lambda out: True, delay=1, retry=3):
    """Runs command until it succeeds. Raises a ValueError if it can't get the program to succeed.

    Args:
        cmd (str): The command to run
        check_output (callable): A function that takes the output string and returns whether or not it's ok
        delay (int|float): The number of seconds to wait between attempts
        retry (int): The number of times to try running the command before failing

    Returns:
        str: The program output. The program return code is guaranteed to be 0.
    """
    for _ in range(retry):
        # Run the given command.
        ret, output = execute_cmd(cmd)
        status_ok = ret == 0
        output_ok = check_output(output)
        if status_ok and output_ok:
            break

        time.sleep(delay)
    else:
        raise ValueError(f"Program failed to execute within {retry} attempts (status: {ret}: {output}")

    # Return value will be cmd output, since the return code is guaranteed to always be 0
    return output