Python3 오류 및 예외


Python 초보자로서 Python 프로그래밍을 처음 배울 때 이전에는 언급하지 않았지만 이 장에서 구체적으로 소개할 것입니다.

Python에는 식별하기 쉬운 두 가지 유형의 오류가 있습니다. 구문 오류와 예외입니다.

구문 오류

Python의 구문 오류, 즉 구문 분석 오류는 초보자에게 자주 발생합니다. 다음 예에서는

>>> while True print('Hello world')
  File "<stdin>", line 1, in ?
    while True print('Hello world')
                   ^
SyntaxError: invalid syntax

이 예에서는 print() 함수 앞에 오류가 있는 것으로 나타났습니다. 콜론(:)입니다.

파서는 오류가 있는 줄을 가리키고 발견된 첫 번째 오류를 작은 화살표로 표시합니다.

Exceptions

Python 프로그램의 구문이 정확하더라도 실행 시 오류가 발생할 수 있습니다. 런타임 중에 감지된 오류를 예외라고 합니다.

대부분의 예외는 프로그램에서 처리되지 않으며 여기에 오류 메시지 형태로 표시됩니다.

>>> 10 * (1/0)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: Can't convert 'int' object to str implicitly

예외는 다양한 유형으로 나타나며 이러한 유형은 정보의 일부로 인쇄됩니다. 예제의 유형은 다음과 같습니다. ZeroDivisionError, NameError 및 TypeError.

오류 메시지 앞부분에는 예외가 발생한 상황이 표시되며, 구체적인 정보가 호출 스택 형식으로 표시됩니다.

예외 처리

다음 예에서는 사용자가 유효한 정수를 입력할 수 있지만 프로그램을 중단할 수도 있습니다(Control-C 또는 운영 체제에서 제공하는 방법 사용). 사용자가 중단한 메시지는 KeyboardInterrupt 예외를 발생시킵니다.

>>> while True:
        try:
            x = int(input("Please enter a number: "))
            break
        except ValueError:
            print("Oops!  That was no valid number.  Try again   ")

try 문은 다음과 같이 작동합니다.

  • 먼저 try 절(키워드 try와 키워드 Except 사이의 문)이 실행됩니다.

  • 예외가 발생하지 않으면 Except 절이 무시됩니다. try 절 문장이 실행된 후 종료됩니다.

  • try 절 실행 중에 예외가 발생하면 나머지 try 절은 무시됩니다. 예외 유형이 Except 뒤의 이름과 일치하면 해당하는 Except 절이 실행됩니다. 마지막으로 try 문 다음에 코드를 실행합니다.

  • 예외가 어떤 예외와도 일치하지 않으면 이 예외는 상위 시도로 전달됩니다.

try 문에는 다양한 특정 예외를 처리하기 위해 여러 개의 Except 절이 포함될 수 있습니다. 최대 하나의 분기가 실행됩니다.

핸들러는 해당 try 절의 예외만 처리하고 다른 try 핸들러의 예외는 처리하지 않습니다.

예외 절은 여러 예외를 동시에 처리할 수 있습니다. 이러한 예외는 튜플을 형성하기 위해 괄호 안에 배치됩니다. 예:

    except (RuntimeError, TypeError, NameError):
        pass

마지막 제외 절은 예외 이름을 무시할 수 있으며 예외로 사용됩니다. 와일드카드 . 이 메서드를 사용하여 오류 메시지를 인쇄한 다음 예외를 다시 발생시킬 수 있습니다.

import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error: {0}".format(err))
except ValueError:
    print("Could not convert data to an integer.")
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise

try Except 문에는 선택적 else 절도 있습니다. 이 절을 사용하는 경우 모든 Except 절 뒤에 배치해야 합니다. try 절에서 예외가 발생하지 않으면 이 절이 실행됩니다. 예:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print('cannot open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()

else 절을 ​​사용하는 것이 모든 문을 try 절에 넣는 것보다 낫습니다. 이는 제외에 의해 포착되지 않는 예상치 못한 예외를 방지하는 것입니다.

예외 처리는 try 절에서 직접 발생하는 예외를 처리할 뿐만 아니라 해당 절에서 호출되는 함수(간접적으로 호출되는 함수도 포함)에서 발생한 예외를 처리합니다. 예:

>>> def this_fails():
        x = 1/0
   
>>> try:
        this_fails()
    except ZeroDivisionError as err:
        print('Handling run-time error:', err)
   
Handling run-time error: int division or modulo by zero

예외 발생

Python은 raise 문을 사용하여 지정된 예외를 발생시킵니다. 예:

>>> raise NameError('HiThere')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: HiThere

raise 유일한 매개 변수는 throw될 예외를 지정합니다. 예외 인스턴스이거나 예외 클래스(즉, Exception의 하위 클래스)여야 합니다.

이 예외가 발생했는지 알고 싶고 이를 처리하고 싶지 않은 경우 간단한 raise 문으로 예외를 다시 발생시킬 수 있습니다.

>>> try:
        raise NameError('HiThere')
    except NameError:
        print('An exception flew by!')
        raise
   
An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in ?
NameError: HiThere

사용자 정의 예외

새로운 예외 클래스를 생성하여 자신만의 예외를 가질 수 있습니다. 예외는 Exception 클래스에서 직접 또는 간접적으로 상속되어야 합니다. 예를 들면 다음과 같습니다.

>>> class MyError(Exception):
        def __init__(self, value):
            self.value = value
        def __str__(self):
            return repr(self.value)
   
>>> try:
        raise MyError(2*2)
    except MyError as e:
        print('My exception occurred, value:', e.value)
   
My exception occurred, value: 4
>>> raise MyError('oops!')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
__main__.MyError: 'oops!'

이 예에서는 Exception 클래스의 기본 __init__()가 재정의됩니다.

<pException 클래스는 다른 클래스와 마찬가지로 무엇이든 할 수 있지만 일반적으로 오류 관련 속성 중 일부만 제공하고 예외를 처리하는 코드에서 이 정보를 쉽게 얻을 수 있어 더 간단합니다. < p="">

다양한 예외를 발생시킬 수 있는 모듈을 생성할 때 일반적인 접근 방식은 패키지에 대한 기본 예외 클래스를 생성한 다음 이 기본 클래스를 기반으로 다양한 오류 상황에 대한 새 클래스를 생성하는 것입니다. 하위 클래스:

class Error(Exception):
    """Base class for exceptions in this module."""
    pass

class InputError(Error):
    """Exception raised for errors in the input.

    Attributes:
        expression -- input expression in which the error occurred
        message -- explanation of the error
    """

    def __init__(self, expression, message):
        self.expression = expression
        self.message = message

class TransitionError(Error):
    """Raised when an operation attempts a state transition that's not
    allowed.

    Attributes:
        previous -- state at beginning of transition
        next -- attempted new state
        message -- explanation of why the specific transition is not allowed
    """

    def __init__(self, previous, next, message):
        self.previous = previous
        self.next = next
        self.message = message

대부분의 예외 이름은 표준 예외 이름 지정과 마찬가지로 "Error"로 끝납니다.


정리 동작 정의

try 문에는 상황에 관계없이 실행될 정리 동작을 정의하는 또 다른 선택적 절이 있습니다. 예:

>>> try:
        raise KeyboardInterrupt
	finally:
        print('Goodbye, world!')
   
Goodbye, world!
KeyboardInterrupt

위의 예에서는 try 절에서 예외가 발생했는지 여부에 관계없이 finally 절이 실행됩니다.

try 절(또는 Except 및 else 절)에서 catch할 예외가 발생하면 finally 절이 실행된 후 예외가 다시 발생합니다.

다음은 더 복잡한 예입니다(동일한 try 문에 Except 및 finally 절 포함).

>>> def divide(x, y):
        try:
            result = x / y
        except ZeroDivisionError:
            print("division by zero!")
        else:
            print("result is", result)
        finally:
            print("executing finally clause")
   
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

사전 정의된 정리 동작

일부 개체는 시스템의 성공적인 사용 여부에 관계없이 표준 정리 동작을 정의합니다. 필요하면 이 표준 정리 동작이 수행됩니다.

이 예제는 파일을 열고 내용을 화면에 인쇄하는 것을 보여줍니다.

for line in open("myfile.txt"):
    print(line, end="")

위 코드의 문제점은 실행 후 파일이 닫히지 않고 열린 상태로 유지된다는 것입니다.

명령문이 포함된 키워드는 파일과 같은 개체가 사용 후 정리 방법을 올바르게 실행하도록 보장할 수 있습니다.

with open("myfile.txt") as f:
    for line in f:
        print(line, end="")

위 코드가 실행된 후 처리 중에 문제가 있더라도 파일 f는 항상 닫힙니다.