14장. 예외 처리
14장. 예외 처리
예외란 말 그대로 프로그램에서 벌어지는 예외적인 상황을 뜻합니다. 예를 들자면 여러분이 파일을 읽고자 할 때 그 파일이 존재하지 않는 경우라던지, 또는 프로그램이 한참 실행중인데 그 파일을 갑자기 지워버렸다던지 하는 경우 등입니다. 이러한 상황을 처리해 주는 것을 예외 처리 라고 합니다.
비슷하게 여러분의 프로그램에 존재하지 않는 명령문이 있을 경우 어떻게 될까요? 이런 경우 파이썬은 손을 들고(raise) 프로그램에 오류(error) 가 있다고 알려 줍니다.
14.1. 오류
간단한 print 함수를 호출하는 상황을 생각해 봅시다. 이 때 print 를 Print 라고 잘못 쳤을경우 어떻게 될까요?
대/소문자 구분에 유의하세요. 이 경우, 파이썬은 구문 오류를 발생 시킵니다.
>>> Print "Hello World" File "<stdin>", line 1 Print "Hello World" ^ SyntaxError: invalid syntax >>> print "Hello World" Hello World | cs |
위와 같이 SyntaxError (구문 오류) 가 발생되었고 오류가 발생한 위치가 표시됩니다. 이것은 이 오류의 오류 핸들러 에 의해 처리되는 것입니다.
14.2. 예외
이번에는 사용자로부터 뭔가를 입력 받는 것을 시도하는(try) 경우를 생각해 봅시다. 이 때 ctrl + d를 누르고 어떻게 되는지 살펴봅시다.
>>> s = raw_input('Enter something --> ') Enter something --> Traceback (most recent call last): File "<stdin>", line 1, in <module> EOFError | cs |
그러면 파이썬은 EOFError 라고 불리우는 오류를 발생시키는데 이때 EOF란 파일의 끝(end of file) 을 의미하며(파일의 끝은 ctrl-d 에 의해 표현됩니다), 갑자기 파일의 끝이 올 것을 예상하지 못했기 때문에 위와 같은 오류가 발생하는 것입니다.
14.3. 예외 처리
예외는 try..except 문을 통해 처리할 수 있습니다. 이것은 try 블록 안에 평소와 같이 명령을 입력하고 예외 상황에 해당하는 오류 핸들러를 except 블록에 입력해 주면 됩니다.
예제 ( exceptions_handle.py 로 저장하세요):
try: text = input('Enter something --> ') except EOFError: print ('Why did you do an EOF on me?') except KeyboardInterrupt: print ('You cancelled the operation.') else: print ('You entered {}'.format(text)) | cs |
실행 결과:
# Press ctrl + d $ python exceptions_handle.py Enter something --> Why did you do an EOF on me? # Press ctrl + c $ python exceptions_handle.py Enter something --> ^CYou cancelled the operation. $ python exceptions_handle.py Enter something --> No exceptions You entered No exceptions | cs |
동작 원리이 예제에서는 예외가 발생할 수 있는 모든 명령문을 try 블록에 넣어 주었으며 오류/예외를 적절하게 처리해 줄 핸들러를 except 절/블록에 넣어 주었습니다. except 절에서는 지정된 한 개의 오류 혹은 예외를 처리할 수도 있고, 괄호로 묶여진 모든 오류/예외 목록을 처리해 줄수도 있습니다. 만일 오류/예외가 지정되지 않은 경우에는 모든 오류/예외를 처리하게 됩니다.
이 때 모든 try 절에는 적어도 한 개의 except 절이 있어야 합니다. 아니면 try 블록을 사용할 아무런 이유가 없겠지요?
만약 어떤 오류나 예외든지 이처럼 처리되지 않는 경우, 기본 파이썬 오류 핸들러가 호출되는데 그러면 이에 의해 프로그램의 수행이 중단되며 해당하는 오류 메시지가 출력됩니다. 위에서 기본 파이썬 오류 핸들러가 어떻게 동작하는지 보았습니다.
또한 try..except 블록에는 추가로 else 절을 붙여줄 수 있습니다. else 절은 어떤 예외도 발생하지 않았을 경우 호출됩니다.
다음 예제에서 예외 객체를 얻어오는 방법과 이를 통해 추가 정보를 알아내는 방법에 대해 알아보겠습니다.
14.4. 예외 발생시키기
raise 문에 오류/예외의 이름을 넘겨 주는 것을 통해 예외를 직접 발생(raise) 시킬 수 있습니다. 그러면 예외 객체가 throw 됩니다.
이 때 발생시킬 수 있는 오류나 예외는 반드시 직접적으로든 간접적으로든 Exception 클래스에서 파생된 클래스이어야 합니다.
예제 ( exceptions_raise.py 로 저장하세요):
class ShortInputException(Exception): '''A user-defined exception class.''' def __init__(self, length, atleast): Exception.__init__(self) self.length = length self.atleast = atleast try: text = input('Enter something --> ') if len(text) < 3: raise ShortInputException(len(text), 3) # Other work can continue as usual here except EOFError: print ('Why did you do an EOF on me?') except ShortInputException as ex: print ('ShortInputException: The input was ' + \ '{0} long, expected at least {1}')\ .format(ex.length, ex.atleast) else: print ('No exception was raised.') | cs |
실행 결과:
$ python exceptions_raise.py Enter something --> a ShortInputException: The input was 1 long, expected at least 3 $ python exceptions_raise.py Enter something --> abc
No exception was raised. | cs |
동작 원리위 예제에서는 ShortInputException 이라고 불리우는 새로운 예외 형식을 직접 하나 만들어 보았습니다. 여기에는 두 개의 필드가 있습니다. 하나는 length 필드로 주어진 입력의길이를 의미하며, 또 하나는 atleast 필드로 프로그램이 요구하는 최소한의 길이를 의미합니다.
이제 except 절에서 as 를 이용하여 해당 오류의 클래스를 좀 더 짧은 이름의 변수로 대신하여 사용할 수 있게 해 줍니다. 여기서 새로 정의한 예외 형식에 정의한 필드와 값의 관계는 마치 함수에서의 매개 변수와 인수의 관계와 비슷합니다. 마지막으로 이 오류를 처리해주는 except절에서는 해당 예외 객체의 length 와 atleast 필드를 이용하여 사용자에게 적절한 결과를 출력해 줍니다.
14.5. Try … Finally 문
프로그램이 파일을 읽고 있는 상황을 가정해 봅시다. 이 때 예외가 발생할 경우, 예외의 발생 여부와 상관없이 파일 객체를 항상 닫아 주도록 할 수는 없을까요? 이를 위해 finally 블록을 사용합니다.
아래 프로그램을 exceptions_finally.py 로 저장하세요:
import sys import time f = None try: f = open("test_txt1.text") # Our usual file-reading idiom while True: line = f.readline() if len(line) == 0: break print (line, sys.stdout.flush()) print ("Press ctrl+c now") # To make sure it runs for a while time.sleep(2) except IOError: print ("Could not find file test_txt1.txt") except KeyboardInterrupt: print ("!! You cancelled the reading from the file.") finally: if f: f.close() print ("(Cleaning up: Closed the file)") | cs |
실행 결과:
$ python exceptions_finally.py Programming is fun Press ctrl+c now ^C!! You cancelled the reading from the file. (Cleaning up: Closed the file) | cs |
동작 원리아주 평범한 파일을 읽는 코드를 작성하였지만, 파일에서 한 줄을 읽어올 때마다 time.sleep 함수를 호출하여 2초씩 멈추게 하는 인위적인 코드를 집어넣어 프로그램이 천천히 실행되도록 해 주었습니다 (파이썬은 원래 굉장히 빠릅니다). 프로그램이 실행중일 때, ctrl + c를 눌러 프로그램을 강제로 중단시켜 봅시다.
그러면 KeyboardInterrupt 예외가 발생되며 프로그램이 종료됩니다. 그러나 프로그램이 종료되기 전에 finally 절이 실행되므로 파일 객체가 항상 닫히게 됩니다.
여기서 print 문 뒤에 sys.stdout.flush() 를 사용하여 화면에 결과를 바로바로 출력하도록 해 주었습니다.
14.6. with 문
try 블록에서 시스템 자원을 가져오고 finally 문에서 이를 해제하여 주는 것은 공통된 패턴입니다. 그렇지만, with 문을 이용하면 이것을 좀 더 깔끔하게 작성해 줄 수 있습니다.
exceptions_using_with.py 로 저장하세요:
with open("test_txt1.text") as f: for line in f: print (line,) | cs |
동작 원리위 예제는 이전의 예제와 동일한 결과를 출력합니다. 차이점은 open 함수를 사용할 때with 문을 사용하였다는 것입니다. 그러면 파일을 직접 닫아 주지 않아도 with open 이 자동으로 파일을 닫아 줍니다.
그러면 with 문은 어떻게 자동으로 이러한 것들을 처리해 주는 것일까요? 우선 with 문은 open문이 반환해 주는 객체를 받아 오는데, 일단 여기서는 이것을 "thefile" 이라고 해 봅시다.
with 문은 항상 thefile.enter 함수를 호출한 뒤 해당 블록의 코드를 실행하며, 실행이 끝난후에는 항상 thefile.exit 가 호출됩니다.
따라서 finally 블록에 써 준 코드가 exit 메소드에 의해 자동적으로 다루어져야 할 경우에만 이를 사용할 수 있을 것입니다. 이런 경우, 위 방법대로 하면 매번 try..finally 문을 명시적으로 쓰지 않고도 같은 일을 할 수 있습니다.
이에 대한 좀 더 자세한 설명은 이 책이 다루는 범위를 벗어납니다. 자세한 설명은 PEP 3431을 읽어 보시기 바랍니다.
14.7. 요약
지금까지 try..except 문과 try..finally 문의 사용법을 배워 보았습니다. 또 사용자 정의예외 형식을 만드는 법과 예외를 일으키는 법에 대해서도 알아 보았습니다.
다음으로, 파이썬 표준 라이브러리에 대해 알아 보겠습니다.