우와한 개발자

[Python] 예외처리 - try/except, raise, 추상클래스, 사용자 정의 예외

by 우와한개발자

1. 예외 클래스

  • 자바는 예외 클래스명이 Exception으로 끝나는 경우가 많고, 파이썬은 Error로 끝나는 경우가 많음 (예: ValueError, TypeError, IndexError)
  • 주요 예외 클래스
BaseException                        ← 모든 예외의 최상위 클래스
├── SystemExit                       ← sys.exit() 호출 시
├── KeyboardInterrupt                ← Ctrl+C 입력 시
└── Exception                        ← 일반 예외의 최상위 (우리가 주로 다루는 것)
    ├── ArithmeticError
    │     └── ZeroDivisionError      ← 0으로 나눌 때
    ├── LookupError
    │     ├── IndexError             ← 리스트 인덱스 초과
    │     └── KeyError               ← 딕셔너리 키 없음
    ├── OSError
    │     ├── FileNotFoundError      ← 파일 없음
    │     └── PermissionError        ← 권한 없음
    ├── TypeError                    ← 타입 오류
    ├── ValueError                   ← 값 오류
    ├── NameError                    ← 변수명 없음
    ├── AttributeError               ← 속성 없음
    ├── ImportError                  ← 모듈 import 실패
    │     └── ModuleNotFoundError
    └── RuntimeError
          └── NotImplementedError    ← 추상메서드 미구현

 

2. 예외처리 : try ~ except

1) 기본 구조

try:
    # 예외가 발생할 수 있는 코드
    result = 10 / 0
except ZeroDivisionError:
    # 예외 발생 시 실행
    print("0으로 나눌 수 없습니다")

 

2) 예외별로 처리

  • 발생 가능한 예외가 여러 개일 경우 자식 클래스부터 작성 (부모가 먼저면 자식 예외가 걸리지 않음)
try:
    value = int(input("숫자 입력: "))
    result = 10 / value
except ValueError:          # 자식 먼저
    print("숫자가 아닙니다")
except ZeroDivisionError:   # 자식 먼저
    print("0으로 나눌 수 없습니다")
except Exception:           # 부모는 마지막
    print("알 수 없는 예외")

 

3) 다중 예외 처리

  • 여러 예외를 하나의 except에서 처리할 때 튜플로 묶음
try:
    value = int(input("숫자 입력: "))
    result = 10 / value
except (ValueError, ZeroDivisionError):
    print("잘못된 입력입니다")

 

4) else

  • 예외가 발생하지 않았을 때 실행
  • try 안에 써도 되지만 정상 실행 코드와 예외 처리 코드를 분리해 직관적으로 구분하기 위해 사용
try:
    result = 10 / 2
except ZeroDivisionError:
    print("0으로 나눌 수 없습니다")
else:
    print(f"결과 : {result}")   # 예외 없을 때만 실행

 

5) 예외 인수

  • 예외 객체를 as 별칭으로 받아 예외 관련 메시지나 값을 확인할 수 있음
try:
    result = 10 / 0
except ZeroDivisionError as e:
    print(f"예외 발생 : {e}")   # division by zero

 

6) finally

  • 예외 발생 여부와 관계없이 항상 실행
  • 주로 리소스 해제(파일 닫기, DB 연결 종료 등)에 사용
try:
    result = 10 / 0
except ZeroDivisionError as e:
    print(f"예외 발생 : {e}")
finally:
    print("항상 실행됨")   # 예외 발생해도 실행

 

⚠️ with문
- finally에서 리소스 해제 코드를 반복 작성하는 불편함을 해소
- with 블록을 벗어나면 자동으로 리소스 해제 (close() 자동 호출)
- 파일, DB 연결 등에 주로 사용
# 기존 방식 — finally로 직접 닫아야 함
try:
    f = open('file.txt', 'r')
    data = f.read()
finally:
    f.close()

# with 방식 — 자동으로 닫힘
with open('file.txt', 'r') as f:
    data = f.read()
# 블록 벗어나면 f.close() 자동 실행​

 

3. 예외 발생 : raise

  • 예외를 강제로 발생시킴
  • 주로 if문 안에서 조건에 따라 사용
def set_age(age):
    if age < 0:
        raise ValueError("나이는 0 이상이어야 합니다")
    return age

try:
    set_age(-1)
except ValueError as e:
    print(e)   # 나이는 0 이상이어야 합니다

 

4. 추상클래스와 추상메서드

1) 예외 발생을 통한 구현

  • 파이썬은 추상클래스 또는 추상메서드를 위한 키워드가 없음
  • 생성자나 메서드에 raise를 이용해 직접 구현 강제
class Shape:
    def area(self):
        raise NotImplementedError("area() 메서드를 구현해야 합니다")

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14 * self.radius ** 2

c = Circle(5)
print(c.area())   # 78.5

 

2) abc모듈 사용한 구현

  • abc 모듈의 @abstractmethod 데코레이터 이용
  • 추상메서드를 구현하지 않으면 객체 생성 자체가 불가능
from abc import ABC, abstractmethod

class Shape(ABC):   # ABC 상속
    @abstractmethod
    def area(self):
        pass

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):   # 반드시 구현해야 함
        return 3.14 * self.radius ** 2

# Shape()   # ❌ TypeError: 추상클래스는 객체 생성 불가
c = Circle(5)
print(c.area())   # 78.5

 

5. 사용자 정의 예외

  • Exception 클래스 또는 그 하위 클래스를 상속받아 생성
class ZeroLengthError(Exception):
    def __init__(self):
        super().__init__('사용자 정의 예외 : 길이가 0입니다.')

def check_length(data):
    if len(data) == 0:
        raise ZeroLengthError()

try:
    check_length([])
except ZeroLengthError as e:
    print(e)   # 사용자 정의 예외 : 길이가 0입니다.

블로그의 정보

우와한개발자 님의 블로그

우와한개발자

활동하기