파이톤으로 예외를 던질 때의 최선의 실천

의 목적


프로그램 라이브러리를 개발하고 데이터 분석 도구를 제작할 때 적절하게 예외 처리를 통해 튼튼하고 오류를 발견하기 쉬운 시스템을 만들 수 있다.파이썬은 다른 언어에 비해 예외적으로 처리하는 비용이 비교적 가볍다 따라서 신속하고 안전한 코드를 적극적으로 활용할 수 있다.

추천 행위


예외 처리를 정의할 때 따라야 할 항목을 총결하였다.

던진 예외는 적당히 서면화해야 한다


가장 중요한 일이야.정의된 예외를 적당한 문서로 정의하십시오.매번 쓰기가 번거롭기 때문에 이름만으로 전달되는 이름을 사용하거나__str__에 상세한 설명을 쓰거나Sphinx 등 문서 자동 생성 도구를 사용하는 것을 추천한다.문서 생성의 자동화에 대해서는 마스터 브랜치를 결합하는 동안 S3에 문서를 공개합니다.를 참조하십시오.

라이브러리 작성 일반 예외 및 모두 상속


이렇게 하면 그 프로그램 라이브러리가 던진 모든 예외를 간단하게 받아들일 수 있다.
class VirtualCoinError(Exception):
    "仮想通貨を扱うライブラリが投げる例外の基底クラス"

class CannotBuyError(VirtualCoinError):
    "仮想通貨が買えなかったときに投げるエラー"

class BrandNotFoundError(VirtualCoinError):
    "指定された銘柄がなかったときに投げるエラー"
이렇게 하면...
try:
    # 仮想通貨に関する何らかの処理
except VirtualCoinError:
    # 仮想通貨ライブラリがエラー吐いたときの共通処理
이렇게 써도 돼요.특히 개발 중, 디버깅에서 이 은혜를 받을 수 있다.가상화폐의 예를 들자면 사실 이유가 있다. 내가 예전에 Slack+GAS에서 사내 가상화폐 거래소 Part1 구현라는 글을 썼기 때문에 그것을 끌어냈을 뿐이다

예외적으로 적절한 도면층에서 처리


내부 처리 오류는 피해야 한다.IndexError와 같은 입력을 원하지 않는 잘못된 관계를 바탕으로 사용자에게 의뢰하세요.

Duck typing 사용


말씀드리자면 Duck Typing 분들은 가십시오Wiki.예외처리와Validation에는 LBYL(Look Before You Leap)과 EAFP(Easier to Ask for Forgivenesthan Permission) 개념이 있는데 주로 전자는'돌다리를 두드리는 방식, 후자는'부딪쳐 부수는 방식'이다.다음은 print object 함수의 실현 방법입니다.코드는 LBYL vs EAFP에서 인용되었다.
def print_object_lbyl(some_object):
    # Check if the object is printable...
    if isinstance(some_object, str):
        print(some_object)
    elif isinstance(some_object, dict):
        print(some_object)
    elif isinstance(some_object, list):
        print(some_object)
    # 97 elifs later...
    else:
        print("unprintable object")

def print_object_eafp(some_object):
    # Check if the object is printable...
    try:
        printable = str(some_object)
    except TypeError:
        print("unprintable object")
    else:
        print(printable)
LBYL 에디션에서는 새로운 대상을 만들 때마다 if문이 추가되는 난관이 있어 오류가 발생할 수 있다.한편, EAFP 버전의 대상인 Object에 __str__를 설치하면 실행할 수 있고 코드도 짧아 가독성이 증가한다.

불법 행위


예외 처리를 할 때 할 수 없는 일을 거꾸로 정리했다.

흐름 제어에 사용

Goto 이런 느낌으로 사용하는 것은 금기다.예를 들어 검색 목록에 해당하는 문자열을 만드는 프로그램을 만들 때 찾을 수 없으면 예외를 버리는 것은 잘못된 것이다.Index를 초과한 경우에는 발생하지 말아야 할 상황으로 예외를 답하라.
def string_finder_wrong(l: Sequence[str], s: str, end: int) -> str:
    result = None 
    for i in range(end):
        if (l[i] == s):
            result = s

    if result is None:
        raise NotFoundError  # 見つからなかったときに例外処理でコントロールするのは良くない

def string_finder_good(l: Sequence[str], s: str, end: int) -> Optional[str]:
    if (len(l) <= end):
        raise IndexError  # OutOfBoundsなので例外を投げる

    result = None 
    for i in range(end):
        if (l[i] == s):
            result = s

    return result  # 該当しなかった場合はNoneを返す

내부 설치 유출의 예외


캡슐화를 유지하기 위해 디자인은 예외죠.예를 들어 URL에서 HTML로 되돌아갈 때 캐시를 사용하려고 합니다.그때는 캐시된 파일이 열리지 않을 때 IOError 던지는 것이 잘못되었다.내부 설치가 변경될 수 있으며 향후 S3에 캐시를 저장한 뒤Athena로 접근할 수도 있다는 것이다.내부 설치에 따라 예외도 달라지기 때문에 설치를 변경할 때마다 이 기능의 사용자는 예외 처리를 덮어써야 한다.
def url2html_wrong(url: str) -> str:
    try:
        with open(CACH_FILE):
            # ファイルの中身を操作する何かしらの操作
    except IOError:
        raise IOError  # 内部でファイルIOを利用していることがバレる。

def url2html_good(url: str) -> str:
    try:
        with open(CACH_FILE):
            # ファイルの中身を操作する何かしらの操作
    except IOError:
        raise CashNotFoundError  # キャッシュ周りでエラー吐いたことが伝わる

총결산


파이톤의 예외 처리를 각양각색의 관점으로 총결하였다.기사에 오류가 있거나 "다른 것도 xx하면 좋은 코드가 될 수 있어요"등의 메시지가 있으면 댓글로 남겨주세요

참고 자료

  • Best Practices for Python Exceptions?
  • Robust exception handling
  • Write Cleaner Python: Use Exceptions
  • The definitive guide to Python exceptions
  • Professional Error Handling With Python
  • 좋은 웹페이지 즐겨찾기