Media Log

1. 예외 처리(Exception Handling)


오늘은 예외 처리(Exception Handling)에 대해 알아보려고 합니다. 여기서 '예외(Exception)'란 어떤 것일까요? 우리가 프로그램을 사용하다 보면 예기치 못한 상황으로 에러가 발생하여 비정상 종료되는 경험을 해보신적이 있으신가요? 예를 들어, 프로그램 내에서 존재하지 않는 파일을 열려고 한다던가 피제수를 0으로 나누려고 하는 등 런타임 도중 발생하는 에러를 예외라고 하며, 이는 프로그램의 작업 수행을 막아버리는 존재입니다. 우선 예외가 발생하는 상황을 한번 보도록 하겠습니다.

>>> 2013 * (1229/0)
Traceback (most recent call last):
  File "<pyshell#0>", line 1, in <module>
    2013 * (1229/0)
ZeroDivisionError: division by zero
>>> open('notfind.txt', 'r')
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    open('notfind.txt', 'r')
FileNotFoundError: [Errno 2] No such file or directory: 'notfind.txt'
>>> lst = [1, 2, 3, 4]
>>> lst[5]
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    lst[5]
IndexError: list index out of range

위를 보시면, 피제수를 0으로 나누려고 하니 ZeroDivisionError란 예외가 발생하고, 존재하지 않는 파일을 열려고 하니 FileNotFoundError란 예외가 발생하며, 위치(index)가 범위를 벗어나면 IndexError란 예외가 발생합니다. 이 밖에도 예외는 여러가지가 있으며 파이썬에서는 이런 예외를 처리하여 실행 도중에 에러가 발생해도 예외를 무시하거나 따로 처리할 수 있도록 try와 except란 녀석을 지원합니다. 한번 보도록 할까요?


2. try~except


파이썬에서 예외를 처리하기 위해서는 try~except절을 사용할 수 있는데, 아래는 이 절의 기본적인 형태입니다.

try:
   예외를 유발할 수 있는 구문
except <예외 종류>:
   예외 처리를 수행하는 구문

위의 형태를 한번 봐보시면, try절의 영역에는 예외가 발생할 수 있는 구문이 들어가며 except절의 영역에서는 try절에서 예외가 발생하였을때 잡아서 처리를 수행하는 구문이 들어갑니다. 우선은 먼저 try~except절이 사용된 예제를 보고 설명해드리도록 하겠습니다. (예외 구조도를 참고하시려면 여기를 클릭하세요)

>>> try:
	a = 10 / 0
except ZeroDivisionError:
	print('제수는 0이 될 수 없습니다!')

제수는 0이 될 수 없습니다!

위의 예제를 보시면, try절의 영역 내에서 ZeroDivisionError 예외가 발생할 때 그 예외를 잡아 처리하는 문장입니다. 피제수를 제수로 나눌 때, 제수가 0일 경우 '제수는 0이 될 수 없습니다!'라는 문장을 출력합니다. 이번에는 다른 예제를 보도록 하겠습니다.

>>> try:
	a = int(input("첫번째 숫자를 입력하세요: "))
	b = int(input("두번째 숫자를 입력하세요: "))
	print("a + b = ", a + b)
except ValueError:
	print('값이 적절하지 않습니다.')

첫번째 숫자를 입력하세요: 50
두번째 숫자를 입력하세요: 이십
값이 적절하지 않습니다.

위 예제에서는 input 내장 함수로 입력받은 값이 적절하지 않은 값이라면 ValueError 예외가 발생하여 '값이 적절하지 않습니다.'라는 문장을 출력시킵니다. 위에서는 a에 50이란 정상적인 값이 들어갔지만, b에는 '이십'이란 문자열을 입력받아 이를 정수로 변환시키려 하니 에러가 발생한 것입니다. 이제는 위 예제들을 적절히 섞어서 여러 개의 예외를 처리하는 예제를 한번 보도록 하겠습니다.

>>> try:
	a = int(input("피제수를 입력하세요: "))
	b = int(input("제수를 입력하세요: "))
	print("a / b = ", a / b)
except ValueError:
	print('값이 적절하지 않습니다.')
except ZeroDivisionError:
	print('제수는 0이 될 수 없습니다!')
	
피제수를 입력하세요: 10
제수를 입력하세요: 0
제수는 0이 될 수 없습니다!

위 예제를 보시면 except절은 한번만 사용될 수 있는게 아니라, 여러번 등장이 가능합니다. 위와 같이 예외 처리 영역을 나눌 수도 있지만, 아래와 같이 예외 처리 영역을 합칠 수도 있습니다.

>>> try:
	a = int(input("피제수를 입력하세요: "))
	b = int(input("제수를 입력하세요: "))
	print("a / b = ", a / b)
except (ValueError, ZeroDivisionError):
	print('제수가 0이거나 값이 적절하지 않습니다.')

피제수를 입력하세요: 10
제수를 입력하세요: 영
제수가 0이거나 값이 적절하지 않습니다.

위 예제에서는 소괄호를 사용하여 튜플을 이용해 예외를 처리합니다. 만약 try절의 영역 내에서 ValueError가 발생해도 예외가 처리되며, ZeroDivisionError가 발생해도 except절의 예외 처리 영역으로 이동합니다. 이 말은 즉슨, 튜플 내에 명시된 에러를 모두 처리하겠다는 말이 됩니다. 한마디로 말하자면 에러를 묶어서 처리하는 방법입니다. 마지막으로, 예외 인스턴스 객체를 통한 예외를 처리하는 방법을 보도록 하겠습니다.

>>> try:
	a = 50 / "이십"
except TypeError as e:
	print('예외:', e.args[0])
	
예외: unsupported operand type(s) for /: 'int' and 'str'

위 예제를 보시면, 에러 메시지를 담은 예외 인스턴스 객체를 e로 받아서 사용하는데 이 변수를 통해서 추가적인 정보를 출력해낼 수 있습니다. 위에선 정수와 문자열 간의 나눗셈 연산은 지원하지 않는다는 에러 메시지가 출력됩니다. 여기까지 잘 따라오셨나요? 그럼 다음으로 넘어가겠습니다.


3. else


except절이 예외가 발생하면 처리하는 영역이라면, else절은 예외가 발생하지 않았을 경우에 작업이 수행되는 영역입니다. 이 else절은 모든 except 절의 가장 마지막에 등장하여야 하며, 필요에 의해 else절을 달 수도 있고 달지 않을 수도 있습니다. 한마디로 선택적으로 사용할 수 있다는 것입니다. 우선은 try~except~else절의 기본적인 형태를 보도록 하겠습니다.

try:
   예외를 유발할 수 있는 구문
except <예외 종류>:
   예외 처리를 수행하는 구문
else:
   예외가 발생하지 않을 경우 수행할 구문

위 try절의 영역에서 예외가 발생하면, except절의 예외 처리 영역으로 이동하지만 예외가 발생하지 않는다면 else절의 영역으로 이동합니다. 우선은 예제를 먼저 보도록 하겠습니다.

>>> try:
	f = open('test.txt', 'r')
except IOError:
	print('파일을 열지 못했습니다.')
else:
	print('test.txt:', f.read())
	f.close()

test.txt: 테스트 파일!

위 예제는 test.txt 파일을 읽기 모드로 여는데 I/O에 관련된 예외가 발생하면 '파일을 열지 못했습니다.'라는 문장이 출력되고, 예외가 발생하지 않았다면 그 파일의 내용을 출력하고 파일을 닫습니다. 간단하죠? 


4. finally


finally절은 예외가 발생하든 말든 실행되는 영역입니다. try 영역이 실행되고 나서, 예외 발생여부와 상관없이 무조건 수행되는 영역입니다. 이 finally절도 else절과 같이 선택적으로 사용할 수 있는 영역입니다. 우선은 try~except~finally절의 기본적인 형태를 보도록 하겠습니다.

try:
   예외를 유발할 수 있는 구문
except <예외 종류>:
   예외 처리를 수행하는 구문
finally:
   예외 발생 여부과 상관없이 항상 실행되는 구문

위 try절의 영역에서 예외가 발생하든 발생하지 않든 finally절 영역의 구문이 실행됩니다. 간단하게 한번 예제를 보도록 하겠습니다.

>>> try:
	a = 10 / 0
except ZeroDivisionError:
	print('제수는 0이 될 수 없습니다!')
finally:
	print('무조건 실행되는 영역!')

제수는 0이 될 수 없습니다!
무조건 실행되는 영역!

위 예제에서는 try절의 영역에서 ZeroDivisionError 예외가 발생하지만, 그것과는 관계없이 finally절의 영역으로 들어가는 것을 보실 수 있습니다. 직접 예제에서 제수가 0이 아닌 다른 수로 바꾸어 예외가 발생하지 않게 되더라도 finally절 영역으로 들어가서 '무조건 실행되는 영역!'이라는 문장을 출력합니다.


5. raise


예기치 못한 상황으로 예외가 발생하는 경우도 있지만, 의도적으로 개발자가 예외를 발생시켜야 할 경우도 있습니다. 이럴 경우 raise 구문을 통하여 해당하는 예외를 강제로 발생시킬 수 있습니다. raise 구문을 사용하는 방법은 아래와 같습니다.

raise [예외]
raise [예외(데이터)]

예를 들어서 'raise TypeError'는 TypeError 예외를 강제적으로 발생시키게 하는 것이고, 'raise NameError("데이터!")'는 TypeError 예외를 강제로 발생시키면서 '데이터!'를 예외의 인자로 지정하는 것입니다. 우선은 먼저 예제를 보고 raise문이 어떤 역할을 하는지 보도록 하겠습니다.

>>> raise NameError
Traceback (most recent call last):
  File "<pyshell#116>", line 1, in <module>
    raise NameError
NameError
>>> raise NameError('예외 발생!')
Traceback (most recent call last):
  File "<pyshell#117>", line 1, in <module>
    raise NameError('예외 발생!')
NameError: 예외 발생!

위 예제를 보시면, raise 구문을 통해 NameError 예외를 강제로 발생시키고 있습니다. 두번째 raise 구문은 예외의 인자를 지정해 준 것으로써, 지정한 인자와 함께 같이 출력이 되는 것을 확인하실 수 있습니다. 다른 예제를 한번 보시죠.

>>> try:
	a = int(input('피제수를 입력하세요: '))
	b = int(input('제수를 입력하세요: '))
	if a <= 0 or b <= 0:
		raise ArithmeticError('피제수 혹은 제수가 0 이하일 수 없습니다.')
except ArithmeticError as e:
	print('예외 발생:', e.args[0])

피제수를 입력하세요: 2030
제수를 입력하세요: -10
예외 발생: 피제수 혹은 제수가 0 이하일 수 없습니다.

위 예제에서는 피제수 혹은 제수가 0 이하일 경우 ArithmeticError 예외를 발생시키도록 하였습니다. 피제수는 0 초과니 조건 성립이 되지 않았지만, 제수가 -10로 0 이하의 수니 조건문이 실행되어 예외를 발생시킨 것입니다. 이해가 되시나요? 아직 이해가 되지 않으신것 같다면 위에 있는 예제들을 응용하여 코드를 여러번 작성하여 보도록 해보세요. 계속 경험과 노력이 쌓이다 보면 그에 따른 좋은 결과가 있으리라 믿습니다.

저작자 표시 비영리 변경 금지
신고
  1. ZiNee at 2014.01.02 07:12 신고 [edit/del]

    예외처리 잘 배웠습니다. 감사합니다. ^^

    Reply
  2. YOONKIM at 2014.03.05 10:13 신고 [edit/del]

    이제 시작하는 입장에서
    정말 좋은 정보 감사드립니다~

    Reply
  3. purble place at 2014.03.26 15:42 신고 [edit/del]

    취미로 배우고있는데 감사합니다.
    IDLE (Python GUI) 기본사용법중에 몇가지 질문드려요.

    1.줄번호표시법
    2.화면청소법(새글로 만들면 메모장이되고, Ctrl+F6은 청소없이 리스타트만되더군요.)

    Reply
  4. dltn at 2014.05.13 21:06 신고 [edit/del]

    우와,...책쓰셔도되겟네요..다음강의빨리업뎃됫으면 ㅠ

    Reply
  5. 이제시작 at 2014.06.02 19:49 신고 [edit/del]

    마지막 강의까지 잘 봤습니다., 이 정도면 기초프로그래밍 단계에 해당되는건 다 본게 맞나요?

    Reply
    • BlogIcon EXYNOA at 2014.06.03 16:42 신고 [edit/del]

      네, 기초 프로그래밍 단계에 있는건 어느정도 아시는 수준이 되신 것이 맞습니다. 그렇지만, 제 강좌가 내용이 부실하여 책 혹은 다른 강좌를 통해서 내용을 보태주시는게 좋습니다.

  6. BlogIcon 김모 at 2014.06.12 17:28 신고 [edit/del]

    휴 이제야 다 봤네요.
    도움 많이 됐습니다. 감사합니다 ^^

    Reply
  7. 민씨 at 2014.08.22 16:28 신고 [edit/del]

    글 너무 잘보았습니다. 정말 중요한 내용 위주로 쉽게 설명해주셔서 감탄했습니다.

    아래 예제를 실행해보았는데요.
    오류가 나네요. 왜 그런지 알 수 있을까요?

    try:
    a = int(input("피제수를 입력하세요: "))
    b = int(input("제수를 입력하세요: "))
    print("a / b = ", a / b)
    except (ValueError, ZeroDivisionError):
    print('제수가 0이거나 값이 적절하지 않습니다.')

    피제수를 입력하세요: 1
    Traceback (most recent call last):
    File "<pyshell#57>", line 2, in <module>
    a = int(input("피제수를 입력하세요: "))
    TypeError: 'str' object is not callable

    Reply
  8. Mini at 2014.09.23 22:51 신고 [edit/del]

    안녕하세요, 좋은 강좌 처음부터 끝까지 정말 잘 보고 갑니다.

    강좌를 보고 따라하는 도중 이런 에러가 뜨는데, 어째서인지 알 수 있을까요?

    >>> try:
    a = int(input("피제수를 입력하세요 : ")
    b = int(input("제수를 입력하세요 : ")

    SyntaxError: invalid syntax

    a 까지는 되는데 b 값을 입력하는 소스를 치면 에러가 나네요..

    Reply
    • BlogIcon EXYNOA at 2014.09.24 01:49 신고 [edit/del]

      괄호가 끝까지 입력되지 않았습니다. 아래와 같이 수정하세요.

      try:
      a = int(input("피제수를 입력하세요 : "))
      b = int(input("제수를 입력하세요 : "))
      ..

  9. j ha at 2014.11.19 18:27 신고 [edit/del]

    끝까지 다 봤네요. 정말 감사합니다. 복받으세요^^

    Reply
  10. 탈출77 at 2015.01.01 17:20 신고 [edit/del]

    안녕하세요 Python 초보자 입니다.
    강의 잘보고 있습니다. 문의 사항이 있습니다.

    try:
    a = int(input("Input First Num : "))
    b = int(input("Input Second Num : "))
    print("a + b = ", a + b)
    except ValueError:
    print('Not Proper')

    위 코드를 실행 후 입력을 'a' 문자로 주면 except 로 잡혀야 되는데요
    아래 처럼 나옵니다. 대신 ValueError 을 제거 하면... 동작 하던데요.
    Python 2.7 에서 실행 했습니다. 다른 무언가가 있는 가요???

    Input First Num : a
    Traceback (most recent call last):
    File "C:\workspace\PYTHON\Hellow\src\exception.py", line 2, in <module>
    a = int(input("Input First Num : "))
    File "C:\eclipse\plugins\org.python.pydev_3.9.0.201411111611\pysrc\pydev_sitecustomize\sitecustomize.py", line 141, in input
    return eval(raw_input(prompt))
    File "<string>", line 1, in <module>
    NameError: name 'a' is not defined

    Reply
    • BlogIcon EXYNOA at 2015.01.04 23:01 신고 [edit/del]

      2.7에서는 python builtin 중 input()이 eval(raw_input())와 같은 기능을 하기 때문에 a라는 변수가 정의되지 않았다고 eval에서 에러를 내뱉는 것입니다. input()를 raw_input()으로 바꾸어서 사용하세요.

  11. at 2015.08.11 12:53 신고 [edit/del]

    안녕하세요! 프로그래밍을 처음 접하는데 글들이 많은 도움이 되었습니다. 감사합니다^^
    질문이 있는데 어디다가 해야할지 몰라서 올려봅니다(실례가된다면 지우겠습니다..)
    파이썬 3.4버전에서
    현재 시간을 표시하려면
    >>>import time
    >>>time.asctime()
    인데
    이걸 IDLE 대화식 인터프리터에서 치면 잘 실행이 됩니다.
    그런데 IDLE 상단메뉴 file 에서 new file 을 만들어
    >>>가 뜨지않는 창에서 작성한 후 ti.py로 저장한 후
    run module 을 누르면 초기 IDLE창에 RESTART만 뜨고 시간이 안뜹니다..

    또 도스창에서 cd C:\
    cd Ptyhon34
    Python ti.py 를 쳐도 실행이 안되구요...
    그냥 바로 ti.py파일을 더블클릭해 실행하면 수행 후 곧바로 도스 창이 닫히니까
    input('Type Enter key..')를 마지막에 치고 저장하면
    도스창에 Type Enter key.. 만 떠요

    그렇다고 대화형 인터프리터에서(>>>가 뜨는창) 저 모든걸 치면 창내에서 줄 바꿀때마다 시간이랑 결과는 바로바로 뜨는데
    저장했을시 더블클릭해 실행하거나 도스창에서 실행하거나 IDLE에서 import ti.py로 실행하거나 Run Module을 하면
    File "ti.py", line 1
    Python 3.4.3 <v3.4.3:9b73f1c3e601, Feb 24 2015, 22:44:40> [MSC v.1600 64 bit <AMD64>] on win34
    ^
    SyntaxError: invalid syntax
    라면서 오류가 나구요..

    달력 띄우는 것인
    import calendar
    calendar.prmonth(2015,8)
    로 똑같이 했을땐 도스창에서 디렉토리로 이동해서 수행하는것도 잘 되고,
    저장한 파일 더블클릭해서 수행하는것도 바로 안꺼지고 달력과 함께 Type Enter key 뜨면서 잘되고
    IDLE에서 RUN MODLUE해서 하는것도 잘되는데... (아 그리고 대화형 인터프리터에서 import cal 쳤을때두요)

    뭐가 문제일까요? ㅠㅠ

    Reply
    • BlogIcon EXYNOA at 2015.11.13 17:16 신고 [edit/del]

      print 함수를 통해 time.asctime()로 반환되는 값을 출력해보시기 바랍니다.
      print(time.asctime())로 수정해보시고 다시 한번 답글 달아주세요.

      두번째 질문은 실행하려는 파이썬 스크립트의 내용이 구체적으로 어떤 것인지 확인할 필요가 있습니다.

  12. 그레이트 at 2015.11.10 10:51 신고 [edit/del]

    감사하네요.
    파이썬에서 ᅟmodule 이름을 어떻게 알아야 되는가요?
    기정으로 제공하는 module은 뭔지 설명해주시면 감사하겠네요.

    Reply
  13. swyshy at 2017.01.25 13:42 신고 [edit/del]

    e.arg[0]는 무슨 용도로 쓰인 것 인가요?

    Reply
  14. lee at 2017.02.02 01:12 신고 [edit/del]

    자세한 설명 감사합니다. 종종 방문하겠습니다 ^^

    Reply

submit

티스토리 툴바