예외 처리

프로그램 실행중에 오류(error)가 발생하면 예외(exception) 상태로 전환된다. 예외 상태로 전환되면 실행중이던 프로그램을 멈추고 발생한 오류를 알려준다. 즉, 예외는 오류를 처리하는 방식이며, C 언어에서는 존재하지 않는다.

예외 처리와 관련해서 다룰 내용은 다음과 같다.

  • 예외의 종류 살펴보기
  • try ... except ... 명령어를 이용하여 예외 처리하기
  • raise 명령어를 이용하여 예외 발생시키기

예외 종류

가장 많이 발생하는 예외들을 살펴본다.

ZeroDivisionError
NameError
IOError
IndexError
NotImplementedError

등등.

ZeroDivisionError

0으로 숫자를 나누려고 할 때 발생한다.


In [1]:
4.5 / 0


---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-1-1349566cb458> in <module>()
----> 1 4.5 / 0

ZeroDivisionError: float division by zero

NameError

선언되지 않은 변수를 사용하고자 할 때 발생한다.


In [2]:
print(abc)


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-2-fb8e540c9088> in <module>()
----> 1 print(abc)

NameError: name 'abc' is not defined

IOError

아직 생성되지 않은 파일에 접근할 때, 저장 공간이 부족할 때, 저장이 제대로 되지 않을 때 등등 발생한다.


In [3]:
f = open('not_yet_defined_file.txt', 'r')


---------------------------------------------------------------------------
IOError                                   Traceback (most recent call last)
<ipython-input-3-46685f98a1fa> in <module>()
----> 1 f = open('not_yet_defined_file.txt', 'r')

IOError: [Errno 2] No such file or directory: 'not_yet_defined_file.txt'

IndexError

시퀀스 자료형의 객체의 원소에 접근할 때 정해진 인덱스 범위를 초과할 때 발생한다.


In [4]:
l = range(3)
l[3]


---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-4-80950de46f70> in <module>()
      1 l = range(3)
----> 2 l[3]

IndexError: list index out of range

NotImplementedError

정의되지 함수를 사용할 때 고의로 에러를 발생키시고자 할 때 사용한다. raise를 이용한다.


In [5]:
def my_complicated_function():
    """아주 복잡하지만 지금 당장 불필요"""
    raise NotImplementedError("아직 정의되어 있지 않음")

In [6]:
my_complicated_function()


---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
<ipython-input-6-dca6cbb480ed> in <module>()
----> 1 my_complicated_function()

<ipython-input-5-43a2159d7e4b> in my_complicated_function()
      1 def my_complicated_function():
      2     """아주 복잡하지만 지금 당장 불필요"""
----> 3     raise NotImplementedError("아직 정의되어 있지 않음")

NotImplementedError: 아직 정의되어 있지 않음

오류 처리를 사용하지 않으면 오류 메시지가 보이지 않을 수도 있음에 주의해야 한다.


In [7]:
def my_complicated_function_1():
    """아주 복잡하지만 지금 당장 불필요"""

In [8]:
my_complicated_function_1()

오류의 종류

이외에 많은 종류의 오류가 있다.

  • 보다 자세한 설명은 아래 사이트를 참조하면 된다.

      https://docs.python.org/2/library/exceptions.html 
  • 아니면 아래와 같이 help 명령어를 사용하여 확인할 수 있다.

      >>> import exceptions
      >>> help(exceptions)
  • 많은 종류의 오류를 무조건 외울 필요는 없다. 대신에 오류가 발생할 때마다 어떤 종류의 오류가 발생하는지 기억해두는 습관을 키워야 한다.

try ... except ... 명령어 이용 예외 처리

오류가 발생하여 프로그램이 임의로 실행 중단되는 것을 방지하기 위해 사용한다.

  • 오류가 발생할 수 있는 명령어 부분을 try 문으로 감싼다.
  • try 문으로 감싼 코드에서 오류가 발생할 경우 예외 처리하는 부분을 except 문으로 감싼다.
  • try 문으로 감싼 코드에서 오류가 발생하지 않으면 except 문으로 감싼 부분은 건너 뛴다.

기본 사용법

아래 코드를 실행하면 두 가지 경우가 발생할 수 있다.

  • myfilename.dat 파일이 존재하는 경우 해당 파일을 열어 한 줄씩 보여준다.
  • myfilename.dat 파일이 존재하지 않는 경우 아래와 같은 메시지가 보여지고 프로그램 실행이 멈춘다.

      """해당 파일을 열수 없습니다. 프로그램을 더 이상 실행할 수 없습니다.
      An exception has occurred, use %tb to see the full traceback.
      SystemExit: 1"""
    
    
    • 위 메시지에서 한글 부분은 예외 처리 코드 중에서 두 개의 print 문이 실행된 결과이다.
    • sys 모듈의 exit 함수를 호출하면 프로그램이 중단된다. exit 함수가 숫자 1을 리턴하도록 하여 문제 때문에 프로그램이 중단되었음을 알리는 기능도 예외 처리에 포함시킨 것을 알 수 있다.
    • 디버깅을 위해 모두 필요한 정보들이 담기도록 예외 처리를 해야 한다.

In [9]:
try:
    f = open('myfilename.dat', 'r') 
except:
    print("해당 파일을 열수 없습니다."), 
    print("프로그램을 더 이상 실행할 수 없습니다.")
    import sys
    sys.exit(1) #a way to exit the programle couldn’t be opened."), 

for line in f.readlines():
    print(line)


해당 파일을 열수 없습니다. 프로그램을 더 이상 실행할 수 없습니다.
An exception has occurred, use %tb to see the full traceback.

SystemExit: 1
To exit: use 'exit', 'quit', or Ctrl-D.

except 문에 오류의 종류를 명시할 수 있다.

위 코드에서 try문에서 해당 파일이 존재하지 않으면 IOError가 발생하므로 except IOError 라고 명령해도 동일한 기능을 수행한다.


In [10]:
try:
    f = open('myfilename.dat', 'r') 
except IOError:
    print("해당 파일을 열수 없습니다."), 
    print("프로그램을 더 이상 실행할 수 없습니다.")
    import sys
    sys.exit(1) #a way to exit the programle couldn’t be opened."), 

for line in f.readlines():
    print(line)


해당 파일을 열수 없습니다. 프로그램을 더 이상 실행할 수 없습니다.
An exception has occurred, use %tb to see the full traceback.

SystemExit: 1
To exit: use 'exit', 'quit', or Ctrl-D.

except 문에 오류 종류와 오류메시지변수를 함께 사용해서 오류에 대한 보다 자세한 설명을 확인할 수 있다.


In [11]:
try:
    f = open('myfilename.dat', 'r') 
except IOError as er:
    print("해당 파일을 열수 없습니다."), 
    print("프로그램을 더 이상 실행할 수 없습니다.")
    print(er)
    import sys
    sys.exit(1) #a way to exit the programle couldn’t be opened."), 

for line in f.readlines():
    print(line)


해당 파일을 열수 없습니다. 프로그램을 더 이상 실행할 수 없습니다.
[Errno 2] No such file or directory: 'myfilename.dat'
An exception has occurred, use %tb to see the full traceback.

SystemExit: 1
To exit: use 'exit', 'quit', or Ctrl-D.

except 문에 사용하는 오류 종류는 정확해야 한다.

아래 코드의 경우는 예외 처리가 제대로 되지 않는다. try문에서 발생하는 오류는 ZeroDivisionError인 반면에 except IOErrorIOError만을 처리하기 때문에 제대로 예외 처리를 못한다.


In [12]:
try:
    a = 1/0
except IOError:
    print("The file couldn’t be opened."), 
    print("This program stops here.")
    import sys
    sys.exit(1) #a way to exit the program


---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-12-1858e1cb7b70> in <module>()
      1 try:
----> 2     a = 1/0
      3 except IOError:
      4     print("The file couldn’t be opened."),
      5     print("This program stops here.")

ZeroDivisionError: integer division or modulo by zero

try ... except .... 문의 보다 다양한 활용방법은 연습문제를 통해 습득할 예정이다.

  • 아래 사이트 참조 가능
      https://wikidocs.net/30

raise 함수

앞에서 NotImplementedError의 예제에서 보았듯이 raise 함수는 오류를 일부러 유발할 때 사용한다.


In [13]:
raise NameError('HiThere')


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-13-93385ba972b1> in <module>()
----> 1 raise NameError('HiThere')

NameError: HiThere

아래 코드에서처럼 raise 함수는 에러 인자를 선택적으로 받을 수 있다.


In [14]:
try:
    raise NameError('HiThere')
except NameError:
    print 'An exception flew by!'
    raise


An exception flew by!
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-14-e6256f74f78e> in <module>()
      1 try:
----> 2     raise NameError('HiThere')
      3 except NameError:
      4     print 'An exception flew by!'
      5     raise

NameError: HiThere

연습문제 1

스페이스로 구분된 숫자들이 저장되어 있는 파일을 인자로 받아서 각 줄별로 포함된 숫자들과 숫자들의 합을 계산하여 보여주는(print) 함수 print_line_sum_of_file(filename)을 작성하라.

예제:

test.txt 파일에 아래 내용이 들어 있다고 가정하면 아래 결과가 나와야 한다.

1 3 5 8
0 4 7
1 18


In [1]: print_line_sum_of_file("test.txt")
out[1]: 1 3 5 8 17
        0 4 7 11
        1 18 19

주의사항: 파이썬 2.7에서 print 명령은 줄바꿈을 무조거 실행하도록 설정되어 있다. 줄바꾸기를 하지 않으려면 print 명령문 뒤에 콤마를 찍어야 한다.

In [1]: print(3), ; print(4)
        3 4            

견본 답안


In [15]:
f = open("test.txt", 'w')
f.write("1 3 5 8\n0 4 7\n1 18")
f.close()

def print_line_sum_of_file(filename):
    g = open("test.txt", 'r')
    h = g.readlines()
    g.close()
    
    for line in h:
        sum = 0
        k = line.split()
        for item in k:
            print(item),
            sum = sum + int(item)
        print(sum)

print_line_sum_of_file("test.txt")


1 3 5 8 17
0 4 7 11
1 18 19

연습문제 2

데이터 파일에 숫자가 아닌 문자열이 포함되어 있을 경우 그 문자열은 무시하도록 print_line_sum_of_file(filename)를 수정하라.

예제:

test.txt 파일에 아래 내용이 들어 있다고 가정하면 아래 결과가 나와야 한다.

1 3 5 8
1 cat 7
coffee


In [1]: print_line_sum_of_file("test.txt")
out[1]: 1 3 5 8 17
        1 cat 7 8
        coffee 0

견본 답안


In [16]:
f = open("test.txt", 'w')
f.write("1 3 5 8\n1 cat 7\ncoffee")
f.close()

def print_line_sum_of_file(filename):
    g = open("test.txt", 'r')
    h = g.readlines()
    g.close()
    
    for line in h:
        sum = 0
        k = line.split()
        for item in k:
            print(item),
            try: 
                sum = sum + int(item)
            except ValueError:
                pass
        print(sum)

print_line_sum_of_file("test.txt")


1 3 5 8 17
1 cat 7 8
coffee 0