Python의 예외처리

예외처리… 쉽게 설명하자면 에러가 떴을 때 어떻게 할 지 코딩하는거라고 보면 된다. 예를 들어서 웹서핑을 하다 보면 보이는 이런게 있는데

대충 이런거. 404 뜨면 404 페이지를 띄우시오 이런 느낌이라고 보면 된다. 당연한 얘기지만 자바스크립트에도 예외처리가 있다. 나중에 함 다뤄보겠음.

나무위키의 정의에 따르면 예외처리는 ‘예외 처리(Exception Handling) 혹은 오류 처리(Trouble Shooting)란 실행 흐름상 오류가 발생했을 때 오류를 그대로 실행시키지 않고 오류에 대응하는 방법을 제시하는 개념이나 하드웨어 구조를 의미한다. 일반적으로 프로그래밍에서 프로그램이 실행 중 특정 문제가 발생했을 때 다른 처리 방식으로 흐름을 옮기는 개념으로 사용한다.’라고 한다. 예를 들어서 계산기를 코딩했는데 사용자가 0으로 나눈다, 그러면 제로디비전이 나온다. 그럴 때 메시지를 띄우거나 걍 0으로 내보내는 등의 방법을 컴퓨터한테 제시하는 것이다. 가끔 본인 백준 풀이에도 나온다.


try except

트라이 캐치 파이널리 레이즈 뭐 많은데 일단 이 두개만 외워둡시다. try에는 실행할 코드가, except에는 에러가 떴을 때 어떻게 처리할지가 들어간다. 예를 들어보자.

import sys

A, B = map(int,sys.stdin.readline().split())

print(A/B)

이 코드 실행하고 0 0쓰면

컴퓨터가 미쳤습니까 휴먼? 한다. 그 왜 그런지는 이 블로그 어딘가에 있는 백준 풀이를 찾아보시면 압니다… 부정과 불능에 대해 설명했는데 몇번문제인지 까먹음. 아무튼… 근데 이거 처리 안하고 내보내면 사용자 입장에서도 그렇게 좋진 않을거란 말이지? 사용법에 0 0 쓰지 마세요라고 하면 꼭 쓰는 사람 나옵니다.

import sys

A, B = map(int,sys.stdin.readline().split())

try:
    print(A/B)
except:
    print('0으로 나눌 수 없습니다!')

이게 파이썬이라 그렇지 자바스크립트였으면 alert 띄웠음… (alert: 알림창 띄우는거) 위 코드 구조도 간단하다. 두 수를 입력받고 A/B를 계산하되, ZeroDivisionError가 뜨는 상황에는 except에 있는 메시지를 띄워라, 이걸로 이해하면 쉽다.

출처: 코딩도장, ▼ 그림 38–1 예외 발생과 except

여기 잘가르칩니다. 함 해보십쇼. 아무튼… 이 그림이 매우 설명이 적절하다. 실행하려고 했더니 에러뜸? 그럼 익셉트로 고고싱! 오케이?

import sys

A, B = map(int,sys.stdin.readline().split())

try:
    print(A/B)
except ZeroDivisionError:
    print('0으로 나눌 수 없습니다!')

에러 종류가 한두개가 아닌데 except 하나로 처리하기가 거시기하면 이런 식으로 오류 종류에 따라 다르게 처리하도록 지정할 수도 있다. 위 코드도 동일한 역할이지만 ZeroDivisionError일 때는 이렇게 하라고 세부적으로 지시한 것. 저 경우 ZeroDivisionError, IndexError 등 여러가지 에러일때 메시지를 다르게 표기할 수 있다.

import sys

A, B = map(int,sys.stdin.readline().split())

try:
    print(A/B)
except ZeroDivisionError as e:
    print('0으로 나눌 수 없습니다!')
    print(e)

except A as e 형식으로 해당 예외에 대한 에러 메시지를 받아올 수도 있다. 그리고 print(e)를 추가하면 여기에 대한 에러 메시지도 출력해준다. 그리고 except Exception as e 형식을 쓰면 모든 예외에 대한 에러 메시지를 출력할 수 있다. 근데 저 코드로 발생시킬 수 있는 에러가 제로디비전 말고 없음…

else&finally

else는 if 친구 아니냐고? 얘네랑도 노나보죠 뭐.

import sys

A, B = map(int,sys.stdin.readline().split())

try:
    x = A/B
except:
    print('0으로 나눌 수 없습니다!')
else: 
    print(x)

이거 되게 웃긴게 else가 except 위로 가면

미쳤습니까 휴먼? 한다. 순서가 바꼈을 뿐인데 말이지.

else는 예외가 발생하지 ‘않았을 때’ 실행할 코드가 들어간다. 즉 저 세개는 각각 1) A/B를 계산하는데 2) 제로디비전 뜨면 0으로 못 나눈다 하고 3) 아님 출력하슈 인 셈.

import sys

A, B = map(int,sys.stdin.readline().split())

try:
    x = A/B
except:
    print('0으로 나눌 수 없습니다!', e)
else: 
    print(x)
finally:
    print('퐈이널리!')

finally는 예외 발생 여부와 상관없이 항상 실행하는 코드다. 저거는 예외 발생됐을 때 결과잖음?

정상적으로 실행됐을때도 저게 나온다.

raise

코딩하다 보면 가끔 예외를 발생시켜야 할 때가 있다. 아무튼 그럴 때 쓰는게 raise다.

import sys

A = int(sys.stdin.readline())

try:
    if A % 2 == 1:
        raise Exception('Odd number!')
except Exception as e:
    print('Error: ', e)
finally:
    print(A)

위 코드는 입력받은 수가 ‘홀수이면’ 예외가 발생하는 코드다. 그럼 저걸 실행해보실까?

홀수, 즉 2로 나누어서 나머지가 1인 수가 들어오면 예외가 발생하게 되어서 예외처리를 하게 된다. 밑에 있는 5는 finally때문에 출력된 것.

예전에 만들었던 프로젝트 제한효소의 경우 지원하는 파일이 FASTA(.fasta)와 Genbank(.gb)인데, 이 경우 두 파일이 ‘아닌’ 다른 파일을 업로드했을 때 예외를 발생시킨 다음 예외처리를 할 수도 있다.

import sys

file = sys.stdin.readline().rstrip()

try:
    if file.find('fasta') == -1 and file.find('gb') == -1:
        raise Exception('지원하는 파일이 아닙니다.')
except Exception as e:
    print('Error: ', e)
else: 
    print('파일 업로드가 완료되었습니다. ')

이놈들 대체 뭘로 묶어야 정상작동 하는건지…

아무튼 이런 식으로 FASTA나 Genbank파일이 아닌 다른 파일을 올리면 예외를 발생시킬 수도 있다. 아마 지금 코딩된거는 FASTA, Genbank 파일에 하나 있을 때 or 여러개 있을 때로 예외처리해서 처리가 되고 있지만, 사실 구체적으로 들어가자면 FASTA, Genbank파일이 아닌 다른 파일이 올라왔을때도 이런 식으로 해야 한다. 근데 어차피 저거 브라우저 띄울때 FASTA랑 Genbank파일만 찾아서 상관 없음.

import sys

def even_number():
    X = int(sys.stdin.readline())
    if X % 2 == 1:
        raise Exception('홀수입니다!')
    print(X)

try:
    even_number()
except Exception as e:
    print(e)

raise는 함수 안에 넣고 예외처리는 밖에서 할 수도 있다. 단, 저게 없이 그냥 함수를 실행하고 홀수를 입력하면 코드가 중단되고 에러가 뜨게 된다.

import sys

def even_number():
    try:
        X = int(sys.stdin.readline())
        if X % 2 == 1:
            raise Exception('홀수입니닷!')
        print(X)
    except Exception as e:
        print('아 사실 그거 홀수였음! ', e)
        raise

try:
    even_number()
except Exception as e:
    print('스크립트에서 문제가 발생하였습니다.', e)

그… 발생시켰던 예외를 다시 발생시킬 수도 있다. 응? 이거 완전 죽은자의 소생 아니냐? 아무튼 저 코드를 실행하면 어떻게 되느냐…

안에서 한번 예외가 발생해서 처리했는데 이걸 밖에서 또 처리한다. 진정한 예토전생이여.

import sys

def even_number():
    try:
        X = int(sys.stdin.readline())
        if X % 2 == 1:
            raise Exception('홀수입니닷!')
        print(X)
    except Exception as e:
        print('아 사실 그거 홀수였음! ', e)
        raise RuntimeError('실행하다가 문제가 발생했습니다. ')

try:
    even_number()
except Exception as e:
    print('스크립트에서 문제가 발생하였습니다.', e)

함수 안에 있는 raise에 다른 예외처리를 지정하고 re-raise 한 결과.

어째 더 정신사나워졌어…

한번에 다 이해하기 당연히 힘들다. 그게 가능한 개쌉고수들은 여기 올 일이 없어요 벌써 버그터진거 해결하고 담탐 하러 갔지… 아니면 카페인 공급중이거나. 일단 try except까지만 알고 있어도 한결 편해질것이다.