본문 바로가기

Computer Science

[Computer Science] 예외처리(exception handling) - if ~else / try~except

예외 처리(exception handling)
예외란 프로그램이 정상적으로 실행될 수 없는 상황을 뜻한다. 
외부 요인을 통제할 수 없다면, 프로그램 안에 예외 상황에 대한 대응책을 마련해두어야 하는것. 

예외 처리의 방법

  1. if 문 사용
    코드를 실행하기 전에 예외 상황을 미리 검사.
    예외 정보를 함수 밖으로 전달하기 위해 return 문과 오류 코드작성
    입력값 검증이 필요하다. 
  2. try 문을 사용
    예외 상황을 미리 검사하지 않는다.
    예외는 함수 호출과 코드 블록을 저절로 빠져나오며, 원하는 위치에서 처리.
  • 예외 처리를 할 때는 if 문과 오류 코드를 이용하기보다는, try 문을 이용하는 것이 좋다.
  • 미리 허락을 구하는 것보다 저지른 후에 용서를 구하는 것이 쉽다.

if문으로 예외 처리

문제점

  1. 오류 코드(예외를 나타내는 값)과 정상 값을 구별하기가 어렵다. 
  2. 함수를 연달아 호출할 때 예외를 함수 밖으로 전달 하기가 불편.
    - 오류 코드와 정상 값을 구별하는 문제
      함수 안에서 예외 처리를 할 때, 함수는 return 문을 이용해 결과를 반환한다.
      그런데 함수 호출 도중에 예외가 발생하면, 예외가 발생한 사실을 함수 밖으로 어떻게 알리는가?
  3. 예외 상황인지 항상 미리 검사해야 함. 

  • 파이썬에서는 루프가 반복 수행하는 내부 구문 바로 다음에 if 없이도 else구문 추가 가능
for i in range(3):
  print('loop : ', i) # 1) 루프반복수행
else : 
  print('Else statement') # 2) else 구문 추가수행
  • for~else 구문에서는 break에 따라 수행여부가 달라진다.
# for~else구분에 break문이 있다면?

for i in range(3):
  print('loop : ', i) # 1) 루프반복수행
  if i == 1:
    break     # break는 어떤 영향을 줄 것인가?
else : 
  print('Else statement') # 2) else 구문 추가수행
  • if/else 구문에서 else는 '앞의 구문이 수행되지 않으면 else구문을 수행하시오' 라는 뜻이다.
  • try/except 구문 또한 이 구문 앞의 try 구문을 수행하다가 예외발생 시, except 구문을 수행하시오' 라는 뜻이다.

예시 1

0이 아닌 정수값을 입력받아 1을 나눈다.  -- 결과: return (1/input(int)

'정수가 아닙니다.'출력하기 -- if input(x) != numeric  -- print ('정수가아닙니다')

0일 경우 '0으로 나눌 수 없습니다' 출력하기 f input(x) ==0 -- print ('0으로 나눌 수 없습니다')

# 데이터 입력
print('0이 아닌 정수를 입력해 주세요:', end=' ')
user_string = input()

# 예외 처리: 입력값이 정수가 아닌 경우 프로그램 종료
if not user_string.isnumeric():
    print(user_string, '은 정수가 아닙니다.')
    exit()   # 프로그램 종료

# 입력값(문자열)을 정수로 변환
user_number = int(user_string)

# 예외 처리: 입력값이 0인 경우 프로그램 종료
if user_number == 0:
    print('0으로 나눌 수 없습니다.')
    exit()   # 프로그램 종료

# 결과 출력
print(1 / user_number)

추가

while문을 이용해서 올바른 데이터를 입력 할때 까지 계속 입력을 요청 할 수 있다.

while(True):  # ❶ 무한 반복
    # 데이터 입력
    print('0이 아닌 정수를 입력해 주세요:', end=' ')
    user_string = input()
    
    # 예외 처리: 입력값이 정수가 아닌 경우 다시 입력
    if not user_string.isnumeric():
        print(user_string, '은 정수가 아닙니다.')
        continue  # ❷ while 문 본문의 시작 지점에서 다시 반복
    
    # 입력값(문자열)을 정수로 변환
    user_number = int(user_string)
    
    # 예외 처리: 입력값이 0인 경우 다시 입력
    if user_number == 0:
        print('0으로 나눌 수 없습니다.')
        continue  # ❷ while 문 본문의 시작 지점에서 다시 반복
    
    break  # ❸ 반복 중지

# 결과 출력
print(1 / user_number)
0이 아닌 정수를 입력해 주세요: 이백
이백 은 정수가 아닙니다.
0이 아닌 정수를 입력해 주세요: 0
0으로 나눌 수 없습니다.
0이 아닌 정수를 입력해 주세요: 200
0.005

 

try 문 : 잠재적인 예외 처리하기

장점: if문의 문제를 모두 해결함

  1. 정상 값과 구별하기 어려운 오류 코드 대신, 예외 객체라는 특별한 정보로 예외를 전달할 수 있다.
  2. 함수 호출속에서 예외가 발생하면 예외 객체가 함수 바깥으로 전달된다.
  3. 예외가 발생할 것을 미리 확인하는 대신, 예외가 발생했을때 처리하는 방법을 따른다. 

방법

1. except 예외 종류 

try:
    예외가 발생할 수 있는 코드 블록
    ...
except 예외종류:
    예외종류에 해당하는 예외가 발생했을 때 실행할 코드 블록
    ...
(필요에 따라 except 절을 추가로 작성)

2. try문의 else절을 이용

else 절에 작성한 코드는 try 문의 본문을 다 실행할 때까지 예외가 발생하지 않은 경우에 실행된다. try 절의 작성 양식에 else 절을 추가

try:
    예외가 발생할 수 있는 코드 블록
    ...
except 예외종류:
    예외종류에 해당하는 예외가 발생했을 때 실행할 코드 블록
    ...
(필요에 따라 except 절을 추가로 작성)
else:
    예외가 발생하지 않은 경우 실행할 코드 블록

 

예시 2

except절을 추가하여 예상 오류코드에 대한 예외 처리 사항을 생성한다. 

>>> 1 / 0 :  Traceback (most recent call last): File "<prtc>", line 1, in <module> ZeroDivisionError: division by zero

>>> 1 / hey: Traceback (most recent call last):  File "<prtc>", line 1, in <module> 1/hey NameError: name 'hey' is not defined

  

# ❶ try 블록에 예외가 일어날 수 있는 코드를 기술한다.

try:
    print('0이 아닌 정수를 입력해 주세요:', end=' ')
    user_number = int(input())
    print(1 / user_number)
    
# ❷ 처리해야 할 예외의 이름과 처리방법을 except 블록에 기술한다.

except ZeroDivisionError:  # 0으로 나누는 오류 처리
    print('0으로 나눌 수 없습니다.')
    
except ValueError:         # int 유형이 될 수 없는 문자열의 오류 처리
    print('입력한 값은 정수가 아닙니다.')
    
    
# 사전확인했던 오류값이 NameError였지만 try문에 들어가면 valueError로 변경되어 올바른 실행이 안되고 아래와 같으 오류가 나온다.    
# except NameError:         
#     print('입력한 값은 정수가 아닙니다.')

'''Traceback (most recent call last):
  File "<prtc>", line 6, in <module>
    user_number = int(input())
ValueError: invalid literal for int() with base 10: 'hey''''

추가

while문을 이용해서 올바른 입력은 반복하여 요구하기

while True:
    try:
        print('0이 아닌 정수를 입력해 주세요:', end=' ')
        user_number = int(input())
        print(1 / user_number)
        break  # 예외가 발생하지 않은 경우, 반복을 빠져나간다
        
    except ZeroDivisionError:
        print('0으로 나눌 수 없습니다.')
        
    except ValueError:
        print('입력한 값은 정수가 아닙니다.')
0이 아닌 정수를 입력해 주세요: 백
입력한 값은 정수가 아닙니다.
0이 아닌 정수를 입력해 주세요: 0
0으로 나눌 수 없습니다.
0이 아닌 정수를 입력해 주세요: 20
0.05

예시 3

try 문에서 else절 사용

while True:
    try:
        print('0이 아닌 정수를 입력해 주세요:', end=' ')
        user_number = int(input())
        result = 1 / user_number
    except ZeroDivisionError:
        print('0으로 나눌 수 없습니다.')
    except ValueError:
        print('입력한 값은 정수가 아닙니다.')
    
    # 예외가 발생하지 않은 경우에만 실행
    else:
        print(result)  # 결과를 출력하고
        break          # 반복을 빠져나간다

예시4

 try 문을 빠져나온 예외 처리하기

# 블록 1: 바깥쪽 try 문
try:
    
    # 블록 2: while 문
    while True:
        
        # 블록 3: 안쪽 try 문
        try:
            print('0이 아닌 정수를 입력해 주세요:', end=' ')
            user_number = int(input())
            result = 1 / user_number
        except ZeroDivisionError:
            print('0으로 나눌 수 없습니다.')
        except ValueError:
            print('입력한 값은 정수가 아닙니다.')
        else:
            print(result)  # 결과를 출력하고
            break          # 반복을 빠져나간다

# 바깥쪽의 try 문에서 KeyboardInterrupt 예외를 처리한다
except KeyboardInterrupt:
    print('Ctrl + C를 누르셨군요.')

예외가 코드 블록 안에서 발생하는 경우 예외 객체는 가장 가까이 있는 try 문이 처리할 때까지 코드 블록을 차례대로 빠져나간다. 이 특징을 이용하면 함수 호출 안에서 일어난 예외를 함수 호출 밖에서 처리할 수 있다.

 

  • assert '조건', '에러메시지' : 조건이 맞지 않는 경우, 에러메시지(Assertion Error)를 띄워준다.
    • assert()는 방어적 프로그래밍(defensive programming) 방법 중 하나이며, 코드를 점검하는데 사용된다.
# 두번째방법. 조건에 맞는 경우, 함수에서 결과값 변수를 반환해주고 break로 빠져나온다.
def variable_return(p_a, p_b):
  is_variable = True
  for i in range(2, min(p_a, p_b) +1):
    if p_a % i == 0 and p_b % i == 0:
      is_variable = False
      break
  return is_variable

# 조건점검
assert variable_return(4,9), '서로소입니다.'
#assert variable_return(3,6), '코드 점검 메시지입니다2.'
assert not variable_return(3,6), '서로소 아닙니다.'

print('variable_return:',variable_return(4,9))    # 변수값 반환
  • try / except / raise / finally 구문에 대해서 익혀보자.
    • 예외처리를 위한 키워드들이다.
      • try : 처리하고자 하는 부분을 넣는다.
      • except : try구문 안에서 발생할 것으로 예상되는 예외를 처리한다.
      • raise : 예외상황일 때 명시적으로 처리한다.
      • finally : 마지막으로 실행하는 코드
  • try / finally : 에러가 발생하더라도 마지막 코드를 수행해야 할때.
  • try / except / else : else 구문을 사용하면 예외구분을 위해 try 구문 안에 들어갈 코드를 최소화시켜 가독성이 좋아진다.
# try / raise / except
try:
  print('try test')
  test = int(input())
  if test < 0:
    raise NotImplementedError   # 예외사항에 대해 처리하는 raise!
  print('test2 : ',test) 

except NotImplementedError:
  print('NotImplemented Error')

 

ref. https://python.bakyeono.net/chapter-9-3.html

 

9.3 예외 처리 | 연오의 파이썬 프로그래밍 입문서

파이썬으로 프로그래밍에 처음 입문하시는 분을 위한 책입니다. 프로그래밍 지식을 갖고 계시지 않더라도 누구나 학습하실 수 있습니다.

python.bakyeono.net