개발 언어/파이썬

에러와 예외

jjiiiinn 2024. 7. 9. 23:57
728x90

에러와 예외

파이썬의 에러와 예외

1. 문법 에러 (SyntaxError)

  • 파싱 에러라고도 불림
  • 파이썬 코드의 구문이 올바르지 않을 때 발생
while True print('Hello world')
# SyntaxError: invalid syntax

SyntaxError: invalid syntax`

특징:

  • 에러가 발생한 위치를 화살표(^)로 표시
  • 파일 이름과 줄 번호 제공

2. 예외 (Exceptions)

  • 문법적으로 올바르지만 실행 중에 발생하는 에러
  • 항상 치명적이지는 않음 (처리 가능)

주요 예외 유형:

  1. ZeroDivisionError
10 * (1/0)
# ZeroDivisionError: division by zero
  1. NameError
4 + spam*3
# NameError: name 'spam' is not defined
  1. TypeError
'2' + 2
# TypeError: can only concatenate str (not "int") to str

예외 메시지의 구조

  1. 스택 트레이스: 예외가 발생한 위치와 문맥 제공
  2. 예외 유형: 발생한 예외의 이름 (예: ZeroDivisionError)
  3. 상세 설명: 예외의 원인에 대한 구체적인 설명

주요 포인트

  1. 문법 에러는 코드 실행 전에 발견됨
  2. 예외는 코드 실행 중에 발생
  3. 예외는 처리 가능하며, 프로그램의 흐름을 제어하는 데 사용될 수 있음
  4. 표준 예외 이름은 내장 식별자

이해:

  • 문법 에러와 예외를 구분하는 것이 중요
  • 예외 메시지를 해석하고 이해하는 능력이 디버깅에 중요
  • 예외 처리를 통해 더 견고한 프로그램 작성 가능

파이썬의 예외 처리

1. try-except 구문

기본 구조:

try:
    # 실행할 코드
except ExceptionType:
    # 예외 처리 코드

작동 방식:

  1. try 절 실행
  2. 예외 발생 시, 해당 except 절로 이동
  3. 예외 없으면 except 절 건너뜀

2. 다중 예외 처리

여러 예외 동시 처리:

try:
    # 코드
except (TypeError, ValueError):
    # 처리

예외 계층 구조 활용:

  • 상위 클래스 예외가 하위 클래스 예외도 포착

3. 예외 정보 활용

예외 인스턴스 접근:

try:
    raise Exception('메시지')
except Exception as e:
    print(type(e))    # 예외 타입
    print(e.args)     # 예외 인자
    print(e)          # 문자열 표현

4. 주요 예외 클래스

  • BaseException: 모든 예외의 기본 클래스
  • Exception: 대부분의 예외 처리에 사용
  • SystemExit, KeyboardInterrupt: 프로그램 종료 관련

5. else와 finally 절

else: 예외가 발생하지 않았을 때 실행
finally: 예외 발생 여부와 관계없이 항상 실행

try:
    # 코드
except ExceptionType:
    # 예외 처리
else:
    # 예외 없을 때 실행
finally:
    # 항상 실행

6. 예외 처리 모범 사례

  1. 구체적인 예외 처리
  2. 예상치 못한 예외는 상위로 전파
  3. 로깅 활용
  4. 필요시 예외 재발생

주요 포인트

  • 예외 처리로 프로그램 안정성 향상
  • 적절한 예외 타입 선택 중요
  • else 절로 예외 없는 경우 명확히 구분
  • 함수 호출 시 발생하는 예외도 처리 가능

파이썬의 예외 일으키기 (Raising Exceptions)

1. raise 문 기본 사용법

raise ExceptionType('Error message')

2. 예외 클래스와 인스턴스

  1. 예외 인스턴스 사용:
raise ValueError('Invalid value')
  1. 예외 클래스 사용 (인자 없이):
raise ValueError  # 'raise ValueError()' 와 동일

3. 예외 다시 일으키기

예외를 잡은 후 다시 발생시키기:

try:
    raise NameError('HiThere')
except NameError:
    print('An exception flew by!')
    raise
  • raise를 인자 없이 사용하면 현재 처리 중인 예외를 다시 발생시킴

4. 주요 포인트

  1. raise는 의도적으로 예외를 발생시킬 때 사용
  2. 예외 클래스나 예외 인스턴스를 인자로 받음
  3. 예외 클래스만 지정하면 기본 생성자 호출
  4. 예외를 잡은 후 다시 발생시켜 상위 레벨에서 처리 가능

5. 사용 예시 및 팁

  1. 사용자 정의 예외 발생:
class CustomError(Exception):
    pass

raise CustomError("This is a custom error")
  1. 조건부 예외 발생:
def divide(a, b):
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b
  1. 예외 체이닝:
try:
    # some code
except SomeException as e:
    raise RuntimeError("A runtime error occurred") from e
  1. 예외 무시하기 (권장되지 않음):
try:
    # some code
except SomeException:
    pass  # 예외를 무시

파이썬의 예외 연쇄 (Exception Chaining)

예외 연쇄는 한 예외가 다른 예외의 직접적인 원인임을 나타내는 메커니즘입니다. 이를 통해 예외의 원인을 더 명확히 추적할 수 있습니다.

1. 자동 예외 연쇄

예외 처리 중 새로운 예외가 발생할 때 자동으로 연쇄됩니다.

try:
    open("database.sqlite")
except OSError:
    raise RuntimeError("unable to handle error")

출력:

FileNotFoundError: [Errno 2] No such file or directory: 'database.sqlite'

During handling of the above exception, another exception occurred:

RuntimeError: unable to handle error

2. 명시적 예외 연쇄

raise ... from ... 구문을 사용하여 명시적으로 예외를 연쇄할 수 있습니다.

try:
    func()
except ConnectionError as exc:
    raise RuntimeError('Failed to open database') from exc

출력:

ConnectionError

The above exception was the direct cause of the following exception:

RuntimeError: Failed to open database

3. 예외 연쇄 비활성화

from None을 사용하여 자동 예외 연쇄를 비활성화할 수 있습니다.

try:
    open('database.sqlite')
except OSError:
    raise RuntimeError from None

출력:

RuntimeError

4. 주요 포인트

  1. 자동 연쇄: except 블록 내에서 새 예외 발생 시 자동으로 연결됨
  2. 명시적 연쇄: raise ... from exc로 직접 연결 가능
  3. 연쇄 비활성화: raise ... from None으로 연결 제거 가능
  4. 목적: 예외의 원인을 더 명확히 추적하고 디버깅을 용이하게 함

파이썬의 사용자 정의 예외 (Custom Exceptions)

사용자 정의 예외를 생성하면 프로그램 특정의 오류 상황을 더 명확하게 표현할 수 있습니다.

1. 기본 사용자 정의 예외 생성

class CustomError(Exception):
    pass
  • 대부분의 사용자 정의 예외는 Exception 클래스를 직접 또는 간접적으로 상속받습니다.

2. 속성을 가진 사용자 정의 예외

class ValueTooSmallError(Exception):
    def __init__(self, message, value):
        self.message = message
        self.value = value

    def __str__(self):
        return f'{self.message}: {self.value}'

3. 예외 계층 구조 만들기

class DatabaseError(Exception):
    pass

class ConnectionError(DatabaseError):
    pass

class QueryError(DatabaseError):
    pass

4. 주요 포인트

  1. 명명 규칙: 대부분 "Error"로 끝나는 이름 사용
  2. 단순성: 일반적으로 간단하게 유지
  3. 속성: 오류에 대한 추가 정보를 제공하는 속성 포함 가능
  4. 계층 구조: 관련 예외들을 그룹화하여 계층 구조 생성 가능

파이썬의 뒷정리 동작 (Cleanup Actions)

finally 절은 예외 처리와 관계없이 항상 실행되어야 하는 코드를 정의하는 데 사용됩니다.
finally 절을 적절히 활용하면 리소스 관리와 예외 상황에서의 안정성을 크게 향상시킬 수 있습니다.
특히 외부 리소스를 다룰 때 finally를 사용하여 리소스 누수를 방지하는 것이 중요합니다

1. finally 절의 기본 사용

try:
    # 실행할 코드
except SomeException:
    # 예외 처리
finally:
    # 항상 실행되는 코드

2. finally 절의 특징

  1. 예외 발생 여부와 관계없이 항상 실행됨
  2. try, except, else 절에서 예외가 발생해도 실행됨
  3. 함수의 return, break, continue 문 실행 전에 실행됨

3. finally 절의 주요 용도

  1. 리소스 해제 (파일 닫기, 네트워크 연결 종료 등)
  2. 임시 상태 정리
  3. 로깅 또는 정리 작업 수행

4. 주의사항

  1. finally 절에서 return 문 사용 시, 이 값이 최종 반환값이 됨
  2. finally 절에서 예외가 발생하면, 이전 예외를 덮어씀

5. 사용 예시 및 팁

  1. 파일 처리:
try:
    f = open('file.txt', 'r')
    # 파일 작업
except IOError:
    print("파일을 열 수 없습니다.")
finally:
    f.close()  # 파일을 항상 닫음
  1. 데이터베이스 연결:
import sqlite3

conn = sqlite3.connect('example.db')
try:
    # 데이터베이스 작업
except sqlite3.Error as e:
    print(f"데이터베이스 오류: {e}")
finally:
    conn.close()  # 연결을 항상 닫음
  1. 임시 상태 정리:
temp_file = 'temp.txt'
try:
    with open(temp_file, 'w') as f:
        # 임시 파일 작업
finally:
    import os
    os.remove(temp_file)  # 임시 파일 항상 삭제

파이썬의 미리 정의된 뒷정리 동작과 with 문

파이썬에서는 특정 객체들이 자동으로 리소스를 해제하는 뒷정리 동작을 정의합니다. with 문은 이러한 객체들을 안전하게 사용할 수 있게 해주는 편리한 구문입니다.

1. with 문의 기본 사용

with context_manager as variable:
    # 작업 수행
  • context_manager: __enter____exit__ 메서드를 구현한 객체
  • 블록이 종료되면 자동으로 __exit__ 메서드 호출 (리소스 해제)

2. 파일 처리 예시

기존 방식:

for line in open("myfile.txt"):
    print(line, end="")

문제점: 파일이 명시적으로 닫히지 않음

with 문 사용:

with open("myfile.txt") as f:
    for line in f:
        print(line, end="")

장점: 블록 종료 시 자동으로 파일 닫힘

3. with 문의 장점

  1. 리소스 자동 해제
  2. 예외 발생 시에도 안전하게 리소스 해제
  3. 코드 간결성 및 가독성 향상

4. 주요 사용 사례

  1. 파일 처리:
with open('file.txt', 'w') as f:
    f.write('Hello, World!')
  1. 데이터베이스 연결:
import sqlite3
with sqlite3.connect('example.db') as conn:
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM users")
  1. 네트워크 연결:
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect(('example.com', 80))
    s.sendall(b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
    data = s.recv(1024)
  1. 잠금(Lock) 관리:
import threading
lock = threading.Lock()
with lock:
    # 임계 영역 코드

5. 사용자 정의 컨텍스트 매니저

class MyContextManager:
    def __enter__(self):
        print("Entering the context")
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print("Exiting the context")
        if exc_type is not None:
            print(f"An exception occurred: {exc_type}, {exc_value}")
        return False  # 예외를 다시 발생시킴

with MyContextManager() as mgr:
    print("Inside the context")

주요 포인트

  1. with 문은 자동 리소스 관리를 제공
  2. 예외 발생 시에도 안전하게 리소스 해제
  3. 코드를 더 간결하고 읽기 쉽게 만듦
  4. 파일, 데이터베이스 연결, 네트워크 소켓 등에 널리 사용
  5. 사용자 정의 컨텍스트 매니저를 만들어 사용 가능

파이썬의 다중 예외 발생 및 처리

Python 3.11부터 도입된 ExceptionGroupexcept* 구문을 사용하여 여러 개의 관련 없는 예외를 동시에 처리할 수 있습니다.

1. ExceptionGroup

ExceptionGroup은 여러 예외 인스턴스를 하나의 그룹으로 묶어 발생시킬 수 있게 해줍니다.

def f():
    excs = [OSError('error 1'), SystemError('error 2')]
    raise ExceptionGroup('there were problems', excs)

try:
    f()
except Exception as e:
    print(f'caught {type(e)}: {e}')

2. except* 구문

try:
    f()
except* OSError as e:
    print("There were OSErrors")
except* SystemError as e:
    print("There were SystemErrors")

3. 중첩된 ExceptionGroup

ExceptionGroup 안에 다른 ExceptionGroup을 포함할 수 있습니다.

def f():
    raise ExceptionGroup(
        "group1",
        [
            OSError(1),
            SystemError(2),
            ExceptionGroup(
                "group2",
                [
                    OSError(3),
                    RecursionError(4)
                ]
            )
        ]
    )

4. 주요 포인트

  1. ExceptionGroup은 여러 예외를 그룹화합니다.
  2. except*ExceptionGroup 내의 특정 타입 예외만 처리합니다.
  3. 처리되지 않은 예외는 다시 발생됩니다.
  4. ExceptionGroup에는 예외 인스턴스만 포함될 수 있습니다 (타입이 아님).

5. 실제 사용 예시

excs = []
for test in tests:
    try:
        test.run()
    except Exception as e:
        excs.append(e)

if excs:
   raise ExceptionGroup("Test Failures", excs)

이 기능은 동시성 프레임워크나 여러 작업을 병렬로 실행하는 경우, 또는 여러 오류를 수집하고 싶을 때 유용합니다.
복잡한 예외 상황을 더 세밀하게 제어하고 처리할 수 있게 해주며, 특히 대규모 애플리케이션이나 라이브러리에서 유용하게 사용될 수 있습니다.

파이썬의 예외 정보 강화 (Enriching Exceptions with Notes)

Python 3.11부터 도입된 add_note() 메서드를 사용하여 예외에 추가 정보를 덧붙일 수 있습니다.
이 기능은 예외 처리 과정에서 더 상세한 컨텍스트 정보를 제공하는 데 유용합니다.

1. add_note() 메서드

add_note(note) 메서드는 예외 객체에 문자열 형태의 노트를 추가합니다.

try:
    raise TypeError('bad type')
except Exception as e:
    e.add_note('Add some information')
    e.add_note('Add some more information')
    raise

출력:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: bad type
Add some information
Add some more information

2. ExceptionGroup과 함께 사용

add_note()ExceptionGroup과 함께 사용하여 각 예외에 컨텍스트 정보를 추가할 때 특히 유용합니다.

def f():
    raise OSError('operation failed')

excs = []
for i in range(3):
    try:
        f()
    except Exception as e:
        e.add_note(f'Happened in Iteration {i+1}')
        excs.append(e)

raise ExceptionGroup('We have some problems', excs)

3. 주요 포인트

  1. add_note() 메서드로 예외에 추가 정보를 덧붙일 수 있습니다.
  2. 추가된 노트는 표준 트레이스백 렌더링에 포함됩니다.
  3. 노트는 추가된 순서대로 표시됩니다.
  4. 이 기능은 예외의 컨텍스트나 디버깅 정보를 제공하는 데 유용합니다.
728x90