Python 에서 예외가 잡혔는지 확인하는 방법

PythonBeginner
지금 연습하기

소개

이 랩에서는 다양한 기술을 사용하여 Python 에서 예외가 포착되었는지 확인하는 방법을 배우게 됩니다. 이 랩은 견고하고 신뢰할 수 있는 Python 코드를 작성하는 데 중요한 측면인 예외 처리에 중점을 둡니다.

이 랩은 try...except 블록을 사용하여 ZeroDivisionError와 같은 오류를 적절하게 처리하는 예외 처리 흐름을 이해하도록 안내합니다. except 블록 내에서 예외가 포착되었음을 나타내는 플래그를 설정하는 방법을 배우게 됩니다. 마지막으로, 이 랩에서는 보다 제어된 예외 처리를 위한 컨텍스트 관리자 (context manager) 사용 개념을 소개합니다.

예외 처리 흐름 학습

이 단계에서는 Python 의 예외 처리에 대해 배우게 됩니다. 예외 처리는 견고하고 신뢰할 수 있는 코드를 작성하는 데 중요한 부분입니다. 이를 통해 프로그램 실행 중에 발생할 수 있는 오류를 적절하게 처리하여 프로그램이 충돌하는 것을 방지하고 사용자 친화적인 경험을 제공할 수 있습니다.

간단한 예시부터 시작해 보겠습니다. 두 숫자를 나누고 싶지만 두 번째 숫자가 0 일 수 있다고 가정해 보겠습니다. 0 으로 나누는 것은 정의되지 않은 연산이며 Python 에서 ZeroDivisionError를 발생시킵니다.

  1. LabEx 환경에서 VS Code 편집기를 엽니다.

  2. ~/project 디렉토리에 division.py라는 새 파일을 만듭니다.

    touch ~/project/division.py
    
  3. division.py 파일을 편집하고 다음 코드를 추가합니다.

    ## division.py
    numerator = 10
    denominator = 0
    
    result = numerator / denominator
    
    print(result)
    
  4. python 명령을 사용하여 스크립트를 실행합니다.

    python ~/project/division.py
    

다음과 유사한 오류 메시지가 표시됩니다.

Traceback (most recent call last):
  File "/home/labex/project/division.py", line 4, in <module>
    result = numerator / denominator
ZeroDivisionError: division by zero

이 오류 메시지는 0 으로 나누려고 시도했기 때문에 ZeroDivisionError가 발생했음을 나타냅니다. 예외 처리가 없으면 프로그램이 갑자기 종료됩니다.

이제 예외 처리를 사용하여 이 오류를 적절하게 처리해 보겠습니다.

  1. try...except 블록을 포함하도록 division.py 파일을 수정합니다.

    ## division.py
    numerator = 10
    denominator = 0
    
    try:
        result = numerator / denominator
        print(result)
    except ZeroDivisionError:
        print("Error: Cannot divide by zero.")
    

    이 코드에서 try 블록은 예외를 발생시킬 수 있는 코드를 포함합니다. try 블록 내에서 ZeroDivisionError가 발생하면 except 블록의 코드가 실행됩니다.

  2. 스크립트를 다시 실행합니다.

    python ~/project/division.py
    

이제 프로그램은 충돌하는 대신 다음을 출력합니다.

Error: Cannot divide by zero.

이는 예외 처리의 기본 구조를 보여줍니다.

  • try 블록은 예외를 발생시킬 수 있는 코드를 묶습니다.
  • except 블록은 잡을 예외 유형과 해당 예외가 발생할 경우 실행할 코드를 지정합니다.

여러 개의 except 블록을 사용하여 여러 유형의 예외를 잡을 수도 있습니다.

## division.py
numerator = 10
denominator = "abc"

try:
    result = numerator / int(denominator)
    print(result)
except ZeroDivisionError:
    print("Error: Cannot divide by zero.")
except ValueError:
    print("Error: Invalid input. Please enter a number.")

이 예제에서는 ValueError 예외 처리기를 추가했습니다. denominator를 정수로 변환할 수 없는 경우 (예: "abc"와 같은 문자열인 경우) ValueError가 발생하고 해당 except 블록이 실행됩니다.

스크립트를 실행합니다.

python ~/project/division.py

출력:

Error: Invalid input. Please enter a number.

예외 처리를 통해 잠재적인 오류를 예상하고 처리하여 보다 견고하고 사용자 친화적인 프로그램을 작성할 수 있습니다.

except 블록에서 플래그 설정

이 단계에서는 except 블록 내에서 플래그를 사용하여 예외 발생 여부에 따라 프로그램의 흐름을 제어하는 방법을 배우게 됩니다. 이는 try 블록의 결과에 따라 다른 작업을 수행해야 할 때 유용한 기술입니다.

이전 예제를 기반으로, 나눗셈이 성공한 경우에만 숫자의 역수를 계산하고 싶다고 가정해 보겠습니다. 플래그를 사용하여 나눗셈이 성공했는지 여부를 나타낸 다음 그에 따라 역수를 계산할 수 있습니다.

  1. LabEx 환경에서 VS Code 편집기를 엽니다.

  2. ~/project 디렉토리의 division.py 파일을 수정하여 플래그를 포함합니다.

    ## division.py
    numerator = 10
    denominator = 2
    success = True  ## 플래그를 True 로 초기화
    
    try:
        result = numerator / denominator
        print("Result of division:", result)
    except ZeroDivisionError:
        print("Error: Cannot divide by zero.")
        success = False  ## 오류가 발생하면 플래그를 False 로 설정
    except ValueError:
        print("Error: Invalid input. Please enter a number.")
        success = False ## 오류가 발생하면 플래그를 False 로 설정
    else:
        print("No exception occurred.")
    
    if success:
        try:
            reciprocal = 1 / result
            print("Reciprocal:", reciprocal)
        except ZeroDivisionError:
            print("Cannot calculate reciprocal of zero.")
    else:
        print("Reciprocal calculation skipped due to error.")
    

    이 코드에서:

    • try 블록 전에 success라는 플래그를 True로 초기화합니다.
    • ZeroDivisionError 또는 ValueError가 발생하면 해당 except 블록 내에서 success 플래그를 False로 설정합니다.
    • try...except 블록 이후에 success 플래그의 값을 확인합니다. 여전히 True이면 예외가 발생하지 않았음을 의미하며 안전하게 역수를 계산할 수 있습니다. 그렇지 않으면 역수 계산을 건너뜁니다.
    • try 블록에서 예외가 발생하지 않으면 실행될 else 블록도 추가했습니다.
  3. 유효한 분모로 스크립트를 실행합니다.

    python ~/project/division.py
    

    출력:

    Result of division: 5.0
    No exception occurred.
    Reciprocal: 0.2
    
  4. 이제 denominator0으로 변경하고 스크립트를 다시 실행합니다.

    ## division.py
    numerator = 10
    denominator = 0 ## Change the denominator to 0
    success = True
    
    try:
        result = numerator / denominator
        print("Result of division:", result)
    except ZeroDivisionError:
        print("Error: Cannot divide by zero.")
        success = False
    except ValueError:
        print("Error: Invalid input. Please enter a number.")
        success = False
    else:
        print("No exception occurred.")
    
    if success:
        try:
            reciprocal = 1 / result
            print("Reciprocal:", reciprocal)
        except ZeroDivisionError:
            print("Cannot calculate reciprocal of zero.")
    else:
        print("Reciprocal calculation skipped due to error.")
    

    스크립트를 실행합니다.

    python ~/project/division.py
    

    출력:

    Error: Cannot divide by zero.
    Reciprocal calculation skipped due to error.
    

이는 except 블록에서 플래그를 사용하여 프로그램의 흐름을 제어하고 예외 발생 여부에 따라 다른 작업을 수행하는 방법을 보여줍니다.

컨텍스트 관리자를 사용하여 제어

이 단계에서는 Python 에서 컨텍스트 관리자를 사용하여 예외 처리를 단순화하고 예외가 발생하더라도 리소스가 제대로 관리되도록 하는 방법을 배우게 됩니다. 컨텍스트 관리자는 파일, 네트워크 연결 및 명시적으로 닫거나 해제해야 하는 기타 리소스를 사용할 때 특히 유용합니다.

컨텍스트 관리자는 __enter____exit__ 메서드를 정의하는 객체입니다. with 문은 컨텍스트 관리자의 컨텍스트 내에서 코드 블록을 실행하는 데 사용됩니다. __enter__ 메서드는 with 블록이 시작될 때 호출되고, __exit__ 메서드는 예외 발생 여부에 관계없이 with 블록이 종료될 때 호출됩니다.

컨텍스트 관리자를 사용하여 파일을 읽는 간단한 예시부터 시작해 보겠습니다.

  1. LabEx 환경에서 VS Code 편집기를 엽니다.

  2. ~/project 디렉토리에 file_handling.py라는 새 파일을 만듭니다.

    touch ~/project/file_handling.py
    
  3. file_handling.py 파일을 편집하고 다음 코드를 추가합니다.

    ## file_handling.py
    
    filename = "my_file.txt"
    
    try:
        with open(filename, "w") as f:
            f.write("Hello, LabEx!\n")
            f.write("This is a test file.\n")
    except IOError as e:
        print(f"An I/O error occurred: {e}")
    
    try:
        with open(filename, "r") as f:
            content = f.read()
            print("File content:")
            print(content)
    except FileNotFoundError:
        print(f"The file '{filename}' was not found.")
    except IOError as e:
        print(f"An I/O error occurred: {e}")
    

    이 코드에서:

    • with open(filename, "w") as f: 문을 사용하여 my_file.txt 파일을 쓰기 모드 ("w") 로 엽니다. open() 함수는 파일 객체를 반환하며, 이 객체는 변수 f에 할당됩니다.
    • with 문은 예외가 발생하더라도 블록이 종료될 때 파일이 자동으로 닫히도록 합니다.
    • f.write() 메서드를 사용하여 파일에 두 줄의 텍스트를 씁니다.
    • 그런 다음 with open(filename, "r") as f: 문을 사용하여 동일한 파일을 읽기 모드 ("r") 로 엽니다.
    • f.read() 메서드를 사용하여 파일의 전체 내용을 읽고 콘솔에 출력합니다.
    • 잠재적인 FileNotFoundErrorIOError 예외를 처리하기 위해 파일 작업을 try...except 블록으로 묶습니다.
  4. 스크립트를 실행합니다.

    python ~/project/file_handling.py
    

    출력:

    File content:
    Hello, LabEx!
    This is a test file.
    

    my_file.txt 파일이 존재하지 않으면 스크립트가 파일을 생성하고 지정된 내용을 씁니다. 파일이 이미 존재하면 해당 내용이 덮어쓰여집니다. with 문은 어떤 경우든 파일이 제대로 닫히도록 합니다.

파일 처리 중에 예외가 발생할 수 있는 또 다른 예를 살펴보겠습니다.

## file_handling.py
filename = "my_file.txt"

try:
    with open(filename, "r") as f:
        for line in f:
            ## Simulate an error during processing
            if "test" in line:
                raise ValueError("Simulated error during processing")
            print("Line:", line.strip())
except FileNotFoundError:
    print(f"The file '{filename}' was not found.")
except ValueError as e:
    print(f"A value error occurred: {e}")
except IOError as e:
    print(f"An I/O error occurred: {e}")

이 예제에서는 줄에 "test"라는 단어가 포함된 경우 ValueError를 발생시켜 오류를 시뮬레이션합니다. 이 오류가 발생하더라도 with 문은 파일이 제대로 닫히도록 합니다.

스크립트를 실행합니다.

python ~/project/file_handling.py

출력:

Line: Hello, LabEx!
A value error occurred: Simulated error during processing

컨텍스트 관리자는 Python 에서 리소스를 관리하고 예외를 처리하는 깨끗하고 신뢰할 수 있는 방법을 제공합니다. 오류가 발생하더라도 리소스가 제대로 해제되도록 하여 코드를 더욱 강력하고 유지 관리하기 쉽게 만듭니다.

요약

이 랩에서는 견고한 코드를 작성하기 위한 중요한 기술인 Python 의 예외 처리에 대해 배우기 시작합니다. 0 으로 나누면 ZeroDivisionError를 발생시켜 프로그램이 종료되는 간단한 나눗셈 예제로 시작합니다. 그런 다음 try...except 블록을 구현하여 ZeroDivisionError를 정상적으로 처리하여 프로그램이 충돌하는 것을 방지하고 대신 유익한 오류 메시지를 출력합니다.