기본 콘텐츠로 건너뛰기

[ML] 결정트리(Decision Tree) 모델

예외처리(try문)와 assert문

예외처리(try문)과 assert문

예외처리(Exception Control)

파이썬 구문에 위배되는 코드의 실행은 에러가 발생합니다(syntax error).

다음의 코드에서 print()함수는 if 문의 블럭내에 존재해야 하는 함수이므로 개행 후 4칸 들여쓰기를 해야 합니다. 그러나 아래 코드는 이 규칙을 위반한 것으로 구문에러의 한 종류인 IndentationError가 발생합니다.

x=5
if x %2 ==0:
print(f'x는 짝수입니다.')
…
IndentationError: expected an indented block

다음은 0에 의한 나눗셈을 실행할 경우 발생하는 에러(runtime error)로서 ZeroDivisionError라고 합니다.

x=5
x/0
 ---------------------------------------------------------------------------
ZeroDivisionError: division by zero

파이썬에서 발생하는 에러는 예외(exception)라하며 위의 두 예와 같이 구문에러(snytax error)와 런타임에러(runtime error)외에 다양한 에러들이 발생합니다. 사실 프로그램을 작성 중에 이러한 예외를 수정하는 과정이 필수적으로 존재합니다. 이 과정에서 위와 같이 그 예외의 종류를 알 수 있다면 수정작업이 보다 쉬어질 수 있습니다.

파이썬은 빈번히 발생하는 예외들에 대해 AssertionError, ImportError, IndexError, KeyError등과 같이 에러의 종류를 미리 지정하고 있습니다. 이러한 예외들이 발생할 경우 작동 중인 프로세스는 중지되고 에러 메지시를 반환합니다. 그러므로 프로그램을 개발할 때 그것을 작동시키기 전에 예외를 발견하고 처리하는 것은 중요한 과정입니다.

파이썬의 내장된 statement(문)인 try~ except~를 사용하여 코드에 대해 예외를 발견하고 처리할 수 있습니다. 이 구문의 기본 형식은 다음과 같습니다.

try:
    명령 1
except[예외의 종류]:
    명령 2
finally:
	명령 3

위 구문에서 except 다음에 예외의 종류는 생략할 수 있습니다. 명령 1이 예외를 발생하지 않는다면 실행되지만 반대인 경우는 명령 2가 실행됩니다. 즉 이 구문에서는 발생한 예외를 처리하기 위해 그 예외의 특성을 알리고 대체 할 수 있는 코드를 명시하기 위해 except 문을 적용합니다. 또한 마지막의 finally문은 옵션으로 예외의 발생여부와 상관없이 출력됩니다.

예외가 발생할 경우의 그에 대한 정보는 sys.exc_info()함수에 의해 확인할 수 있습니다.

x=['a', 0, 2]
for order, val in enumerate(x):
    try:
        print(f"x[{order}]: {val}, 그 수의 역수: {1/int(val)}")
    except:
        print(f"x[{order}]의 예외의 종류: {sys.exc_info()[0]}")
x[0]의 예외의 종류: <class 'ValueError'>
x[1]의 예외의 종류: <class 'ZeroDivisionError'>
x[2]: 2, 그 수의 역수: 0.5

위 코드에서 객체 x의 각 요소에 대해 실행됩니다. try 문에서 에러가 발생하면 except 문이 실행됩니다. 문자 'a'는 int()함수에 의해 정수로 전환할 수 없습니다. 이러한 알파벳을 정수로 전환하기 위해서는 int() 대신 ord()함수를 사용하여야 합니다.

ord('a')
97

그러므로 ValueError가 발생합니다. 또한 객체 x의 두번째 원소인 0인 경우 그 수로 나눗셈 연산을 실행할 수 없으므로 ZeroDivisionError가 발생합니다.

다음 코드와 같이 except 다음에 예외의 종류를 명시하는 경우 발생하는 예외에 따라 결과가 출력됩니다.

x=['a', 0, 2]
for order, val in enumerate(x):
    try:
        print(f"x[{order}]: {val}, 그 수의 역수: {1/int(val)}")
    except ValueError:         # ①
        print("Value Error")
    except ZeroDivisionError:  # ②
        print("ZeroDivisionError")  
Value Error
ZeroDivisionError
x[2]: 2, 그 수의 역수: 0.5

try 문은 위와 같이 본질적으로 예외가 발생하는 명령에 대해 실시할 수 있습니다. 그러나 고의적으로 예외를 발생시켜야 하는 경우 if문raise() 함수를 사용할 수 있습니다. 예를 들어 키를 입력하는 과정에서 음의 값으로 적는 경우 일반적인 상황에서는 예외로 고려되지 않습니다. 이러한 경우 raise() 함수를 사용할 수 있습니다.

raise 예외종류("에러메시지"): "에러메시지"는 옵션입니다.
raise ZeroDivisionError
 --------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
~~, in <cell line: 1>()
----> 1 raise ZeroDivisionError
ZeroDivisionError:
raise ZeroDivisionError("0으로 나눌 수 없습니다.")
 --------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
~~, in <cell line: 1>()
----> 1 raise ZeroDivisionError("0으로 나눌 수 없습니다.")
ZeroDivisionError: 0으로 나눌 수 없습니다.

raise() 함수를 try 구문에 적용합니다.

hig=-170 
try:
    if hig <= 0:
        raise ValueError("Height must be positive number.") 
except ValueError as ve:
    print(ve,'again!')
Height must be positive number. again!

try 구문 중 finally문을 사용하여 예외를 조정한 값을 반환할 수 있습니다.

hig=-170 
try:
    if hig <= 0:
        raise ValueError("Height must be positive number.") 
except ValueError as ve:
    print(ve,'again!')
finally:
    hig *= -1
    print(hig)
Height must be positive number. again!
170
hig
170

예외무시

try~except 구문에서 예외의 종류를 명확히 하는 것은 코드 작성시 매우 편리한 방법입니다. 반대로 발생되는 예외를 무시하는 경우가 존재합니다. 예를 들어 g(x)=\frac{1}{\sqrt{x-2}}$의 그래프를 작성해야 하는 경우 분모는 0을 포함한 양수이어야 하는 조건을 만족해야 합니다. x의 범위 [-5, 5)에서 그래프를 작성한다고 합시다. try~except 구문과 pass문을 결합하여 이 범위에서 g(x)의 분모가 음수가 되는 부분을 필터링할 수 있습니다.
g=1/sqrt(x-2)
a=np.arange(-5,5)
a1=[]
b=[]
for i in a:
    try:    #(1)
        b.append(float(g.subs(x, i)))   #(2)
        a1.append(i)                            #(3)
    except:  #(4)
        pass  #(5)
a1
[3, 4]
위 코드에서 (1)의 try 문 블럭의 명령들 중 (2)가 실행되면 2의 실행중 사용된 i가 객체 a1에 추가됩니다. 명령 (2)에서 예외(에러)가 발생하면 코드 (4) 블럭으로 이관되며 그 블럭내의 pass 명령에 의해 어떤 작업 없이 다음으로 진행됩니다. 이 코드의 결과는 최초 전달한 x축의 값(a) 중에서 두 개만 나타나게 됩니다. 위 코드를 함수로 작성할 수 있습니다.
def validX(x, f, symbol):
    a=[]
    b=[]
    for i in x:
        try:
            b.append(float(f.subs(symbol, i)))
            a.append(i)
        except:
            pass 
    return(a, b)
validX(a, g, x)
([3, 4], [1.0, 0.7071067811865476])

assert 문

Python의 assert 문은 조건을 테스트하는 디버깅 보조 도구입니다. assert 조건이 true이면 아무 일도 일어나지 않고 프로그램은 계속해서 정상적으로 실행됩니다. 그러나 조건이 false로 평가되면 선택적 오류 메시지와 함께 AssertionError 예외가 발생합니다.

다음은 할인된 금액을 나타내는 사용자 정의 함수입니다. 할인율은 100%를 초과할 수 없기 때문에 이 부분에 대해 assert문을 적용합니다.

def apply_discount(product, discount):
    price=int(product['price']*(1.0-discount))
    assert 0<=price<=product['price']
    return price
shoes={'name':'운동화', 'price':31500}
apply_discount(shoes, 0.25)
23625
apply_discount(shoes, 1.5)
 --------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
~~, in <cell line: 1>()
----> 1 apply_discount(shoes, 1.5)
~~, in apply_discount(product, discount)
      1 def apply_discount(product, discount):
      2     price=int(product['price']*(1.0-discount))
----> 3     assert 0<=price<=product['price']
      4     return price
AssertionError:

assert 문이 포함된 경우 전체 코드 중 정확히 에러가 발생한 코드라인을 찾을 수 있습니다. 이것은 디버깅 속도를 개선시키며 유지, 보수를 보다 쉽게 할 수 있도록 도움을 줍니다. 물론 assert 문은 실행중 발생하는 런타임 오류를 처리하기 위해 사용할 수 있습니다.

assert 문의 기본 구조는 다음과 같습니다.

assert expression1 ["," expression2]

expression1은 테스트하는 조건이고 선택적인 expression2는 그 조건이 실패할 경우 표시되는 오류 메시지입니다. 다음은 인수가 정수인 경우에 대해 assert 문을 적용한 것입니다.

def test(t):
    assert type(t) is int, '정수 아닌 값이 있네'
    print(t)
t1=[2, 6, 5, 1.2]
for i in t1:
    test(i)
2
6
5
--------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
~~, in <cell line: 2>()
      1 t1=[2, 6, 5, 1.2]
      2 for i in t1:
----> 3     test(i)
~~, in test(t)
      1 def test(t):
----> 2     assert type(t) is int, '정수 아닌 값이 있네'
      3     print(t)
AssertionError: 정수 아닌 값이 있네

위 예에서 t1의 요소 1.2에서 조건이 "거짓"이므로 AssertionError가 발생했습니다. 이러한 반환은 조건 자체가 실행되었기 때문에 판단이 가능한 것입니다. 만약 조건 자체가 실행되지 않는다면, 즉 조건에서 예외(에러)가 발생한다면 assert문 자체가 실행될 수 없습니다. 다음 코드의 경우 조건인 나눗셈에서 분모가 0이므로 ZeroDivisionError가 발생하므로 assert 문자체가 실행되지 않습니다.

assert 2/0, '분모는 0이 아니어야 합니다.'
 --------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
~~, in <cell line: 1>()
----> 1 assert 2/0, '분모는 0이 아니어야 합니다.'
ZeroDivisionError: division by zero

클래스를 이용하여 예외 종류 재정의

다음 함수 validation(num)은 인수가 숫자형인지를 검사하는 것입니다. 이 함수에는 isintance(객체, 자료형) 함수를 적용하였습니다. 이 함수는 객체의 자료형과 인수로 전달한 자료형과 일치하면 True를 반환합니다.

isinstance(2, str)
False
def validation(num):
    if isinstance(num, str):
        raise ValueError
validation('2')
 --------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
~~, in <cell line: 1>()
----> 1 validation('2')
~~, in validation(num)
      1 def validation(num):
      2     if isinstance(num, str):
----> 3         raise ValueError
ValueError: 
validation(2)

위 결과와 같이 일으킨 예외의 종류는 raise에 지정한 값입니다. 이 예외를 사용자에 친숙하게 예외의 이름을 수정하여 봅니다.

class dataTypeError(TypeError):
    pass

def validation(num):
    if isinstance(num, str):
        raise dataTypeError
validation('2')
--------------------------------------------------------------------------
dataTypeError                             Traceback (most recent call last)
~~, in <cell line: 1>()
----> 1 validation('2')
~~, in validation(num)
      4 def validation(num):
      5     if isinstance(num, str):
----> 6         raise dataTypeError
dataTypeError: 

댓글

이 블로그의 인기 게시물

[Linear Algebra] 유사변환(Similarity transformation)

유사변환(Similarity transformation) n×n 차원의 정방 행렬 A, B 그리고 가역 행렬 P 사이에 식 1의 관계가 성립하면 행렬 A와 B는 유사행렬(similarity matrix)이 되며 행렬 A를 가역행렬 P와 B로 분해하는 것을 유사 변환(similarity transformation) 이라고 합니다. $$\tag{1} A = PBP^{-1} \Leftrightarrow P^{-1}AP = B $$ 식 2는 식 1의 양변에 B의 고유값을 고려한 것입니다. \begin{align}\tag{식 2} B - \lambda I &= P^{-1}AP – \lambda P^{-1}P\\ &= P^{-1}(AP – \lambda P)\\ &= P^{-1}(A - \lambda I)P \end{align} 식 2의 행렬식은 식 3과 같이 정리됩니다. \begin{align} &\begin{aligned}\textsf{det}(B - \lambda I ) & = \textsf{det}(P^{-1}(AP – \lambda P))\\ &= \textsf{det}(P^{-1}) \textsf{det}((A – \lambda I)) \textsf{det}(P)\\ &= \textsf{det}(P^{-1}) \textsf{det}(P) \textsf{det}((A – \lambda I))\\ &= \textsf{det}(A – \lambda I)\end{aligned}\\ &\begin{aligned}\because \; \textsf{det}(P^{-1}) \textsf{det}(P) &= \textsf{det}(P^{-1}P)\\ &= \textsf{det}(I)\end{aligned}\end{align} 유사행렬의 특성 유사행렬인 두 정방행렬 A와 B는 'A ~ B' 와 같

[matplotlib] 히스토그램(Histogram)

히스토그램(Histogram) 히스토그램은 확률분포의 그래픽적인 표현이며 막대그래프의 종류입니다. 이 그래프가 확률분포와 관계가 있으므로 통계적 요소를 나타내기 위해 많이 사용됩니다. plt.hist(X, bins=10)함수를 사용합니다. x=np.random.randn(1000) plt.hist(x, 10) plt.show() 위 그래프의 y축은 각 구간에 해당하는 갯수이다. 빈도수 대신 확률밀도를 나타내기 위해서는 위 함수의 매개변수 normed=True로 조정하여 나타낼 수 있다. 또한 매개변수 bins의 인수를 숫자로 전달할 수 있지만 리스트 객체로 지정할 수 있다. 막대그래프의 경우와 마찬가지로 각 막대의 폭은 매개변수 width에 의해 조정된다. y=np.linspace(min(x)-1, max(x)+1, 10) y array([-4.48810153, -3.54351935, -2.59893717, -1.65435499, -0.70977282, 0.23480936, 1.17939154, 2.12397372, 3.0685559 , 4.01313807]) plt.hist(x, y, normed=True) plt.show()

R 미분과 적분

내용 expression 미분 2차 미분 mosaic를 사용한 미분 적분 미분과 적분 R에서의 미분과 적분 함수는 expression()함수에 의해 생성된 표현식을 대상으로 합니다. expression expression(문자, 또는 식) 이 표현식의 평가는 eval() 함수에 의해 실행됩니다. > ex1<-expression(1+0:9) > ex1 expression(1 + 0:9) > eval(ex1) [1] 1 2 3 4 5 6 7 8 9 10 > ex2<-expression(u, 2, u+0:9) > ex2 expression(u, 2, u + 0:9) > ex2[1] expression(u) > ex2[2] expression(2) > ex2[3] expression(u + 0:9) > u<-0.9 > eval(ex2[3]) [1] 0.9 1.9 2.9 3.9 4.9 5.9 6.9 7.9 8.9 9.9 미분 D(표현식, 미분 변수) 함수로 미분을 실행합니다. 이 함수의 표현식은 expression() 함수로 생성된 객체이며 미분 변수는 다음 식의 분모의 변수를 의미합니다. $$\frac{d}{d \text{변수}}\text{표현식}$$ 이 함수는 어떤 함수의 미분의 결과를 표현식으로 반환합니다. > D(expression(2*x^3), "x") 2 * (3 * x^2) > eq<-expression(log(x)) > eq expression(log(x)) > D(eq, "x") 1/x > eq2<-expression(a/(1+b*exp(-d*x))); eq2 expression(a/(1 + b * exp(-d * x))) > D(eq2, "x") a * (b * (exp(-d * x) * d))/(1 + b