티스토리 뷰

Python

Python, 예외처리

hwangyoungjae 2016. 4. 29. 13:09
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

프로그램의 제어흐름을 조정하기 위해 사용하는 이벤트를 예외(Exception)라고 함.

기본적으로 파이썬에서는 아무런 처리를 하지 않는 예외에 대하여 자동으로 에러(Error)를 일으키며사용자의 제어흐름을 벗어난 에러문을 출력하고 프로그램을 종료함.

>>> a = [1,2,3]

>>> a[3] #리스트 그기를 벗어난 인덱스참조

Traceback (most recent call last):

  File "<pyshell#431>", line 1, in <module>

    a[3]

IndexError: list index out of range

>>> 

위처럼 비정상적인 종료를 일으키는 예외를 '처리되지 않은 예외(Unhandled Exception)'라고 함.

 

>> 구문에러 <<

개발자가 새로운 언어를 배울때 가장 많이 일으키는 에러가 구문에러(Syntax Error)이다.

파이썬에서는 오타들여쓰기의 실수로 인해 구문에러가 주로 발생하지만인터프리터에서 일반적으로 구문에러가 의심되는 부분을 개발자에게 알려주기때문에 쉽게 수정할수 있다.

>>> print('a)

  File "<stdin>", line 1

    print('a)

              ^

SyntaxError: EOL while scanning string literal

>>> 

파이썬 인터프리터는 첫따옴표를 만난 이후에 다음 따옴표를 만날때까지 출력문자열을 검색함.

하지만 위의 경우 종료를 나타내는 따옴표가 없기 때문에 문장의 가장 마지막에서 구문에러가 발생함

>>> for t in [1,2,3]

  File "<stdin>", line 1

    for t in [1,2,3]

                      ^

SyntaxError: invalid syntax

위와 같은경우도 for문에서 하위구문으로 진입하기 위한 ":"문자가 없기때문에 구문에러가 발생

 

>> 예외 <<

구문 에러 없이 잘 작성된 코드라도 실행도중에 에러가 발생할수 있음.

구문에러외에 쉽게 발생할수 있는 예외 몇가지를 보도록 하겠음

-. NameError

 a라는 변수를 찾지 못하여 발생함

>>> print(a)

Traceback (most recent call last):

  File "<pyshell#462>", line 1, in <module>

    print(a)

NameError: name 'a' is not defined

 

-. ZeroDivisionError

정수를 0으로 나눌때 발생함

>>> 10/0

Traceback (most recent call last):

  File "<pyshell#469>", line 1, in <module>

    10/0

ZeroDivisionError: division by zero

 

-. IndexError

리스트의 인덱스범위를 넘어선 참조시 발생함

>>> a = [1,2,3]

>>> a[3]

Traceback (most recent call last):

  File "<pyshell#471>", line 1, in <module>

    a[3]

IndexError: list index out of range

 

-. TypeError

정수 나누기 문자, type맞지 않는 연산으로 인해 발생함

>>> a = 'apple'

>>> 10 /a

Traceback (most recent call last):

  File "<pyshell#485>", line 1, in <module>

    10 /a

TypeError: unsupported operand type(s) for /: 'int' and 'str'

 

이렇게 발생하는 예외는 exceptions모듈에 미리 정의되어 있음.

또한 내장 이름공간에도 이미 포함되어 있기 때문에특별히 import할 필요는 없음

 

아래는 기본이 되는 예외클래스

클래스이름

내용

Exception

모든 내장 예외의 기본이 되는 클래스

사용자정의 예외를 작성하고자 한다면이 클래스를 상속받아 구현해야 함

ArithmeticError

수치 연산 에러의 기본이 되는 내장예외

LookupError

쉬퀸스 관련 에러의 기본이 되는 내장예외

EnvironmentError

File IO와 같은 파이썬 외부 에러의 기본이 되는 내장예외

 

아래는 실제 발생되는 내장예외클래스

클래스이름

내용

AssertionError

'assert'구문이 실패하는 경우 발생

AttributeError

속성의 참조나 할당에서 실패하는 경우 발생

EOFError

input()계열의 함수로 읽은 내용이 없이 EOF가 입력된 경우 발생

ex) input()함수를 수행하고 아무런 입력없이 'Ctrl+d'를 누르면 발생

FloatingPointError

부동 소수점 연산이 실패하는 경우 발생

pyconfig.h WANT_SIGFPE_HANDLER가 정의되거나,

--with-fpectl옵션이 설정된 경우만 발생함

GeneratorExit

제네레이터(generator) close()메서드가 호출되는 경우 발생

IOError

open()과 같은 I/O연산이 실패하는 경우 발생

ImportError

'import'관련 구문에서 실패하는 경우 발생

IndexError

시퀸스 계열 객체의 인덱스가 범위를 벗어난 경우 발생

KeyError

사전에서 키를 찾지 못한경우 발생

KeyboardInterrupt

사용자가 인터럽트키(Ctrl+c/Delete)를 누른경우 발생

MemoryError

할당할 메모리가 없는 경우 발생

NameError

지역,전역 이름공간중에서 유효하지 않은 이름을 접근하는 경우 발생

NotImplementedError

RuntimeError의 파생 예외로부모클래스에 정의된

추상메서드를 자식클래스에서 오버라이드하지 않은 경우 발생

OSError

시스템 관련 에러

OverflowError

산술 연산결과가 표현할수 있는 범위를 벗어난 경우 발생

ReferenceError

약한 참조 프록시에서 발생하는 예외

RuntimeError

프로그램이 작동중 분류할수 없는 경우 발생

StopIteration

next()나 이터레이터의 __next__()에 대하여 더는 반환할 값이 없는 경우 발생

SyntaxError

구문오류시 발생

SystemExit

sys.exit()함수가 호출되는 경우 발생

이 예외가 처리되지 않는 경우프로그램은 종료됨

TypeError

부적절한 타입의 객체에 값을 할당하는 경우 발생

UnicodeError

UnicodeEncodeError

UnicodeDecodeError

UnicodeTranslateError

유니코드와 연관된 예외들

ValueError

자료형에 대하여 타입은 올바르나 값이 적절하지 않은 경우 발생

ZeroDivisionError

나머지 연산에서 제수가 '0'인 경우 발생하는 예외

 

>> 예외처리 <<

try구문을 이용하면 발생가능성이 있는 예외를 적절하게 처리할수 있다.

기본적인 try구문의 사용방법은 아래와 같다.

try:

<예외 발생가능성이 있는 문장>

except <예외 종류>:

<예외 처리 문장>

except (예외1,예외2):

<예외 처리 문장>

except 예외 as 인자:

<예외 처리 문장>

else:

<예외가 발생하지 않은 경우수행할 문장>

finally:

<예외 발생 유무에 상관없이 try블록 이후 수행할 문장>

예외 발생이 예상되는 부분에 대하여 'try'블록에 작성하고 예외발생시 처리를 담당하는 부분을 'except'블록에 작성하면 됨

except블록은 예외처리 방법에 따라 3가지 방식으로 작성할수 있음

만약 예외가 발생하지 않은 경우 'else'블록의 문장이 수행되며 예외 발생과 상관없이 'finally'블록의 문장은 항상 수행됨

'else'블록과 'finally'블록은 선택사항으로 생략할수 있음

 

순서도로 표현하면 아래와 같음


 

-. 예외처리 예제

def divide(a,b):

    return a /b

 

try:

    c = divide(5,'string')

except ZeroDivisionError:

    print('두번째 인자는 0이면 안됨')

except TypeError:

    print('모든 인수는 숫자이어야 함')

except:

    print('무슨 에러인지 모르겠음')

 

실행결과

모든 인수는 숫자이어야 함

 

위의 문장에서 예외처리는 각 except문장을 순차적으로 검사함

즉 발생한 예외에 대해서 'ZeroDivisionError'인지 우선 비교를 하고

다음은 'TypeError'를 비교함

앞의 두 경우가 모두 아닌경우에 마지막인 'except'구문을 통하여 발생한 예외를 처리함

이는 예외처리는 반드시 좁은 범위에서 넓은 범위로 확장해야 함을 의미함.

 

-. 책임사슬(Chain of Responsibilty)

GoF패턴 중 에러처리에 유연하게 적용할수 있는 구조이다.

에러를 해결할수 있는 에러처리기(Error Handler)를 일렬로 늘어놓은 후만약 에러가 발생하는 경우 순차적으로 각 에러처리기가 해결할수 있으면 해결을 하고 할수 없는 경우 다음으로 에러를 전달하는 방식이다.

예를 들어 부피를 알수 없는 흙을 옮기는데 " -> 꽃삽 ->  -> 포크레인"순으로 시도를 해보고 처리를 하는 방식이다만약 한손의 분량으로 옮길수 있음에도 포크레인으로 처리를 한다면 에러처리에 드는 오버헤드가 커져 비효율적이 된다.

그러므로 위에서 설명한것과 같이 예외처리는 반드시 좁은 범위에서 넓은 범위로 확장해야 한다.

 

 

-. as구문을 이용하여 예외인스턴스객체의 추가적인 정보를 출력하기

as구문을 이용하여 예외인스턴스 발생시 추가적인정보를 변수에 대입후 출력을 할수 있다.

def divide(a,b):

    return a /b

 

try:

    c = divide(5,'string')

except TypeError as e:

    print('모든 인수는 숫자이어야 함')

    print(e)

 

실행결과

모든 인수는 숫자이어야 함

unsupported operand type(s) for /: 'int' and 'str'

 

 

-. tuple을 이용하여 에러를 묶어서 처리하기

except에 대한 에러지정시 tuple을 이용하여 한가지의 예외가 아닌 여러가지의 예외를 지정하여

여러 에러를 묶어서 동시에 처리할수가 있다.

def divide(a,b):

    return a /b

 

try:

    c = divide(5,'string')

except (TypeError,OverflowError,FloatingPointError):

    print('수치 관련 에러발생')

 

실행결과

수치 관련 에러발생

 

-. 상위 예외클래스를지정하여 하위예외를 모두 처리하기

ZeroDivisionError가 포함되어 있는 부모클래스를 호출하여 상위클래스로 하여금 하위클래스트예외를 모두 처리하게끔 하는 방법이다.

def divide(a,b):

    return a /b

 

try:

    c = divide(5,0)

except ArithmeticError:

    print('수치 관련 에러발생')

 

실행결과

수치 관련 에러발생

위와 같이 ArithmeticError클래스에는 수치관련예외들이 포함되어 있다.

따라서 부모클래스를 직접 호출하여 하위에 있는 ZeroDivisionError를 처리할수 있는것이다.

 

>> raise구문 <<

프로그래머가 의도적으로 예외를 발생시켜야 하는 경우도 있다.

이러한 경우 'raise'구문을 사용하면 된다.

raise구문의 형식을 아래와 같다

 

raise [Exception] #해당 예외를 발생함

 

raise [Exception(data)] #예외를 발생시 관련 데이터를 전달함

 

raise #발생된 예외를 상위로 전달함

 

raise구문은 해당 예외를 단순히 발생시키거나 예외 발생시 필요한 정보를 같이 전달하기 위하여 인자로 넘겨줄수 있다또한 예외를 상위에서 처리하도록 그대로 전달할수도 있다. raise구문의 예외로 올수 있는 것으로 내장예외와 사용자정의 예외가 있다.

 

사용자정의 예외를 생성하는 경우반드시 내장 클래스인 Exception을 상속받아서 정의햐여야 한다.

 

첫번째예제는 내장예외를 명시적으로 발생시키는 경우이다.

 

>내장예외발생

def RaiseErrorFunc():

    raise NameError

 

try:

    RaiseErrorFunc()

except:

    print('NameError is Catched')

 

실행결과

NameError is Catched

함수 RaiseErrorFunc내에서 raise구문을 이용하여 NameError를 발생시키며이렇게 발생한 에러는 try~except구문에서 처리된다.

아래는 예외를 상위로 전달(Propagaion)하는 예제이다.

 

>내장예외전달

def RaiseErrorFunc():

    raise NameError("NameError의 인자")

 

def PropagateError():

    try:

        RaiseErrorFunc()

    except:

        print("에러전달 이전에 먼저 이 메시지가 출력됨")

        raise

PropagateError()

 

실행결과

에러전달 이전에 먼저 이 메시지가 출력됨

Traceback (most recent call last):

  File "D:\Cloude\Python\예외처리예제.py", line 10, in <module>

    PropagateError()

  File "D:\Cloude\Python\예외처리예제.py", line 6, in PropagateError

    RaiseErrorFunc()

  File "D:\Cloude\Python\예외처리예제.py", line 2, in RaiseErrorFunc

    raise NameError("NameError의 인자")

NameError: NameError의 인자

PropagateError()함수가 호출되고 내부의 RaiseErrorFunc()함수도 호출됨

RaiseErrorFunc()함수에 의해서 NameError에러가 발생하며처리하는 부분이 없기 때문에 발생한 에러는 PropagateError()함수로 전달됨

예외를 받은 PropagateError()함수에서는 화면에 메세지를 출력하고 받은 예외를 그대로 상위로 전달함 전달된 예외는 처리하는 부분이 없기 때문에 출력결과에서 확인할수 있듯이에러를 출력함.

출력결과에서 예외가 발생할때의 콜스택(Call Stack)정보와 raise구문에서 전달한 인자를 확인할수 있다.

 

>> 사용자정의 예외 <<

개발을 하다보면 내장예외만으로는 표현할수 있는 한계가 있다나눗셈을 수행하는 함수의 경우에 대하여 제수가 '0'보다 작은 경우 사용자정의 예외를 발생시키는 예제를 통해 사용자정의 예외에 대해서 알아보겠다.

 

>사용자정의예외

class NegativeDivisionError(Exception):

    def __init__(self,value):

        self.value = value

 

def PositiveDivide(a,b):

    if(b < 0) : #제수가 0보다 작은경우, NegativeDivisionError발생

        raise NegativeDivisionError(b)

    return a/b

 

try:

    ret = PositiveDivide(10,-3)

    print('10/3={0}'.format(ret))

except NegativeDivisionError as e: #사용자정의 예외인경우

    print('Error - Second argument of PositiveDivide is ',e.value)

except ZeroDivisionError as e: #0으로 나눈 경우

    print('Error - ',e.args[0])

except: #그 외 모든 예외의 경우

    print(e.args)

 

실행결과

Error - Second argument of PositiveDivide is  -3

모든 사용자 정의 예외는 내장 예외인 Exception클래스나 그 하위클래스를 상속받아 구현되어야 하며전달해야할 인자가 있는 경우에는 생성자에서 클래스 멤버변수를 이용하여 저장할수 있다이렇게 정의된 클래스는 개발자가 원하는 경우에 raise구문으로 예외를 발생시킬수 있다.

 

>> assert구문 <<

예외를 발생시키는 또 다른 방법으로 assert문을 이용할수 있다.

일반적으로 assert구문은 개발과정에서 제약사항을 설정할 목적으로 사용하며인자로 받은 조건식이 거짓인경우 AssertionError가 발생한다.

형식 : assert <조건식>, <관련데이터>

위의 표현식은 아래와 동일함

if __debug__:

if not <조건식>:

raise AssertionError(<관련데이터>)

<관련 데이터>는 예외가 발생한 경우 AssertionError의 인자로 전달되며생략가능하다.

또한 내부 변수인 __debug__ True인 경우에만 assert구문이 활성화되며, False인 경우에는 assert구문은 수행되지 않는다.

__debug__변수를 설정하기 위하여 명령프롬프트에서 파이썬 코드를 실행할때최적화옵션(-O)을 설졍하면 __debug__의 값은 False가 된다.

아래 예제는 입력받은 인자에 10을 곱해서 돌려주는 함수이다인자는 오직 정수형만 고려하였기 때문에 그 이외의 경우는 assert구문으로 AssertionError가 발생하도록 하였다.

또한 Error발생시 관련 정보가 출력되도록 문자열 정보를 AssertionError의 인자로 전달하고 있다.

 

>assert예제

def foo(x):

    assert type(x) == int,"Input value must be integer"

    return x * 10

 

ret = foo('a'#AssertionError발생

print(ret)

 

실행결과

Traceback (most recent call last):

  File "D:\Cloude\Python\예외처리예제.py", line 5, in <module>

    ret = foo('a') #AssertionError발생

  File "D:\Cloude\Python\예외처리예제.py", line 2, in foo

    assert type(x) == int,"Input value must be integer"

AssertionError: Input value must be integer

 

최적화옵션(-O)을 설정하고 코드를 수행하면 assert 구문이 수행되지 않기 때문에 "return x * 10"문장이 정상적으로 수행되어 아래와 같은 결과가 출력된다.

C:\>python.exe -O 예외처리예제.py

aaaaaaaaaa

 

 

참조 : 빠르게 활용하는 파이썬프로그래밍

'Python' 카테고리의 다른 글

Python,module copy [객체복사]  (0) 2016.04.29
Python,module re [정규표현식]  (0) 2016.04.29
Python, 파일 입출력  (0) 2016.04.29
Python,module glob  (0) 2016.04.29
Python,module os.path [파일의 경로명]  (0) 2016.04.21
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/06   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
글 보관함