CodingTest/Python Grammar Notes

[Python] 에러와 예외 (try, except, raise ..)

조 수빈 2022. 10. 18. 16:20

🔍 에러와 예외(try, except, raise..)

그동안 알고리즘 문제를 풀 때 인덱스를 벗어나는 등의 오류가 발생했을 때 범위를 제한하거나 다른 풀이를 모색해왔는데, 친구가 "try except문 쓰면 되는 거 아니야?"라고 말해준 뒤로, 정말 많이 애용하고 있다.(그동안 왜 생각을 못 했지..)

파스칼의 삼각형 문제(SWEA 2005번)를 풀면서 try-except를 사용하여 인덱스가 음수인 경우 예외 처리를 해주었는데, 파이썬에서는 인덱스가 음수여도 배열 끝을 기준으로 요소에 접근할 수 있다는 것이 문제였다.
'인덱스가 음수인 경우 아예 의도적으로 예외를 발생시킬 순 없을까?' 하고 찾아본 결과 raise문을 사용하면 해결이 가능함을 알게 되었고, 이참에 정리를 해놓아야겠다 생각이 들어 작성해 본다.

추가) ROT13(백준 4446번) 문제의 경우 입력이 여러 줄로 이루어져 있다고 하며, 별도의 종료 문자열이 주어지지 않았다.
EOF(End of File, 파일의 끝)를 판단해야 하는데, 파이썬에서 EOF를 판단할 때 자주 사용하는 방법이 try, except를 사용하는 것이라고 한다.

while True:
    try:
        a, b = map(int, input().split())
        print(a + b)

    except:
        break

공식 문서코딩 도장, 점프 투 파이썬 등을 참고하였고, try와 except 그리고 raise 등에 대해서 정리해 보았다.


📃 문법 에러와 예외

공식 문서에서는 문법 에러와 예외 두 개를 구분해 놓음

문법 에러: 말 그대로 문법을 잘못 작성했을 때 발생하는 에러
ex) while 문 작성 시 콜론(":")을 빼먹은 경우

예외: 문장이나 표현식이 문법적으로 올바르다 할지라도, 실행하려고 하면 에러가 발생될 수 있는데, 실행 중에 감지되는 에러를 예외 라고 부름
ex) ZeroDivisionError, NameError, TypeError 등 실행 중에 감지되는 다양한 에러들(예외)
  10 * (1/0) => ZeroDivisionError: division by zero
  4 + spam*3 => NameError: name 'spam' is not defined
  '2' + 2 => TypeError: can only concatenate str (not "int") to str

파이썬에서는 이러한 예외가 발생하면 프로그램을 중단하고 오류 메시지를 보여줌


❌✔️ 예외 처리 기법 (try, except)

예외가 발생하더라도 프로그램의 중단 없이 계속 실행하게 해주는 방법

기본적으로 try, except를 사용함

try:
    실행할 코드
except:
    예외가 발생했을 때 처리하는 코드

try 블록 수행 중 예외가 발생하면 except 블록이 수행됨
(try 블록에서 예외가 발생하지 않는다면 except 블록은 수행 X)

# ex) 숫자를 0으로 나누었을 때
try:
    x = int(input('나눌 숫자: '))
    y = 10 / x
    print(y)
except: # 예외가 발생했을 때 실행 (여기서는 x값으로 0이 입력되었을 때 실행)
    print('예외가 발생했습니다.')    

🔣 특정 예외만 처리하기

위와 같이 try, except 문을 사용했을 경우, 무슨 예외가 발생하든 예외 처리를 해줌
하지만 except에 예외 이름을 지정하여 특정 예외만 따로 처리할 수 있음

try:
    실행할 코드
except 예외이름:
    예외가 발생했을 때 처리하는 코드
y = [100, 200, 300]

try:
    idx, x = map(int, input('인덱스와 나눌 숫자를 순서대로 입력: ').split())
    print(y[idx] / x)
except ZeroDivisionError:    # 숫자를 0으로 나눠서 에러가 발생했을 때 실행
    print('숫자를 0으로 나눌 수 없음!')
except IndexError:           # 범위를 벗어난 인덱스에 접근하여 에러가 발생했을 때 실행
    print('잘못된 인덱스 접근!')

# 인덱스는 맞게 입력하고(0 ~ 2 값) 나누는 숫자에 0을 입력한 경우, ZeroDivisionError 에러가 발생함
# 입력: 1 0     출력: 숫자를 0으로 나눌 수 없음!

# 인덱스를 틀리게 입력하고(ex 3) 나누는 숫자는 0이 아닌 숫자를 입력한 경우, IndexError 에러가 발생함
# 입력: 3 5     출력: 잘못된 인덱스 접근!

📥 예외의 에러 메시지 받아오기

except에서 as 뒤에 변수를 지정하면 발생한 예외의 에러 메시지를 받아올 수 있음
참고) 보통 예외(exception)의 e를 따서 변수 이름을 e로 지음

try:
    실행할 코드
except 예외 as 변수:
    예외가 발생했을 때 처리하는 코드
# ex) 숫자를 0으로 나누었을 때
try:
    x = int(input('나눌 숫자: '))
    y = 10 / x
    print(y)
except ZeroDivisionError as e: 
    print('숫자를 0으로 나눌 수 없습니다.', e)

# 입력: 0       출력: 숫자를 0으로 나눌 수 없습니다. division by zero     

📍 그 외(else, finally)

else의 경우 오류가 없을 경우에만 수행됨

try:
    ...
except [발생 오류[as 오류 메시지 변수]]:
    ...
else:  # 오류가 없을 경우에만 수행됨
    ...

finally의 경우 try 문에 사용하는 것이며, try 문 수행 도중 예외 발생 여부에 상관없이 항상 수행됨
보통 사용한 리소스를 close 해야 할 때 많이 사용된다고 함

f = open('foo.txt', 'w')
try:
    # 무언가를 수행한다.
finally:
    f.close()

🔧 오류 일부러 발생시키기

파이썬은 raise 명령어를 사용해 오류를 강제로 발생시킬 수 있음

# 내가 작성했던 파스칼의 삼각형 문제 풀이 코드의 일부
# 인덱스가 음수인 경우 의도적으로 오류를 발생시켰음
...
for j in range(n):
    tmp = []
    for k in range(j + 1):
        try:
            x = result[j - 1][k - 1] + result[j - 1][k]
            if j - 1 < 0 or k - 1 < 0:
                raise Exception
            tmp.append(x)
        except:
            x = 1
            tmp.append(x)
    result.append(tmp)