Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 352 – 예외에 필요한 슈퍼클래스

Author:
Brett Cannon, Guido van Rossum
Status:
Final
Type:
Standards Track
Created:
27-Oct-2005
Python-Version:
2.5
Post-History:


Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

Python 2.4 이하에서는 어떤 (클래식) 클래스든 예외로 발생시킬 수 있습니다. 2.5에서는 새 스타일 클래스를 허용할 계획이었지만, 이렇게 하면 문제가 더 심각해집니다. 어떤 클래스(또는 인스턴스)든 발생시킬 수 있게 되기 때문입니다! 이렇게 되면 예외의 인터페이스에 대해 아무것도 보장할 수 없으므로 문제가 됩니다. 이 PEP는 발생시키는 모든 객체가 상속해야 하는 새로운 슈퍼클래스를 도입할 것을 제안합니다. 이러한 제약을 두면 믿고 사용할 수 있는 예외의 표준 인터페이스가 생깁니다. 또한 모든 예외가 따라야 하는 잘 알려진 계층 구조도 생깁니다.

특정 인터페이스를 위해 특정 베이스 클래스를 요구하는 것은 파이썬답지 않다고 반박할 수도 있습니다. 그러나 예외라는 특수한 경우에는 타당한 이유가 있습니다(python-dev에서도 대체로 합의된 내용입니다). 계층 구조를 요구하면 except *: 대신 except BaseException:이라고 작성하여 모든 예외를 명시적으로 잡을 수 있으므로, 예외를 잡으려는 코드에 도움이 됩니다. [1]

예외를 위한 새 슈퍼클래스를 도입하면 예외 계층 구조를 조금 더 나은 방향으로 재배치할 기회도 생깁니다. 현재는 내장 네임스페이스에 있는 모든 예외가 Exception을 상속합니다. 여기에는 애플리케이션의 예외 처리에서 제외해야 하는 경우가 많은 두 예외(KeyboardInterrupt와 SystemExit)가 포함되므로 문제가 됩니다. 트레이스백 없이 인터프리터를 종료하는 기본 동작이 애플리케이션이 할 수 있는 어떤 처리보다도 대개 더 바람직하기 때문입니다(>>> 프롬프트로 Python의 대화형 명령 루프를 흉내 내는 애플리케이션은 예외일 수 있습니다). 이 두 예외가 Exception 대신 공통 슈퍼클래스를 상속하도록 바꾸면, 지나치게 넓게 잡지 않고 위로 전파되어야 하는 예외는 잡지 않는 except 절을 쉽게 작성할 수 있습니다.

이 PEP는 PEP 348을 위해 앞서 진행된 작업을 바탕으로 합니다.

공통 슈퍼클래스 요구하기

이 PEP는 새 스타일 클래스이며 단일 속성 args를 가지는 BaseException이라는 새 예외를 도입할 것을 제안합니다. 아래는 Python 3.0에서 이 예외가 동작할 방식을 나타낸 코드입니다(Python 2.x에서 동작할 방식은 Transition Plan 절에서 다룹니다):

class BaseException(object):

    """Superclass representing the base of the exception hierarchy.

    Provides an 'args' attribute that contains all arguments passed
    to the constructor.  Suggested practice, though, is that only a
    single string argument be passed to the constructor.

    """

    def __init__(self, *args):
        self.args = args

    def __str__(self):
        if len(self.args) == 1:
            return str(self.args[0])
        else:
            return str(self.args)

    def __repr__(self):
        return "%s(*%s)" % (self.__class__.__name__, repr(self.args))

하위 호환성을 위해 args에 전달할 수 있는 값에는 제한을 두지 않습니다. 하지만 실제로는 문자열 인자 하나만 사용해야 합니다. 그래야 예외의 문자열 표현이 사람이 읽을 수 있는, 예외에 관한 유용한 메시지로 유지됩니다. __str__ 메서드가 길이 1인 args 값을 특수하게 처리하는 이유가 이것입니다. 프로그램에서 사용할 정보(예: 오류 코드 번호)를 포함하려면 서브클래스의 별도 속성에 저장해야 합니다.

raise 문은 전달되는 모든 객체가 BaseException을 상속하도록 요구하게 바뀝니다. 이렇게 하면 모든 예외가 BaseException을 기점으로 하는 단일 계층 구조 안에 속하게 됩니다 [1]. 또한 BaseException으로부터 상속되는 기본 인터페이스도 보장됩니다. raise에 대한 변경은 Python 3.0부터 강제됩니다(아래의 Transition Plan을 참조하십시오).

BaseException이 예외 계층 구조의 루트가 되므로, 이제 Exception은 BaseException을 상속합니다.

예외 계층 구조 변경

예외 계층 구조에 기본 루트가 생겨 그 중요성이 더욱 커졌으므로, 기존 계층 구조를 변경할 필요가 있습니다. 현재는 오류를 알리는 동시에 인터프리터가 종료되어도 된다는 의미가 아닌 모든 예외를 잡으려면, except 절에 두 예외를 뺀 나머지 예외를 모두 일일이 지정하거나, 두 예외를 따로 잡아 다시 발생시키고 나머지 모든 예외는 아무것도 지정하지 않은 except 절로 넘어가게 해야 합니다:

except (KeyboardInterrupt, SystemExit):
    raise
except:
    ...

이는 불필요하게 장황합니다. 이 PEP는 KeyboardInterrupt와 SystemExit가 BaseException을 직접 상속하도록 옮길 것을 제안합니다.

- BaseException
  |- KeyboardInterrupt
  |- SystemExit
  |- Exception
     |- (all other current built-in exceptions)

이렇게 하면 Exception 예외를 잡는 것이 더 합리적이 됩니다. 오류를 나타내는 예외만 잡게 됩니다. 인터프리터가 종료되어야 함을 알리는 예외는 잡히지 않으므로, 상위로 전파되어 인터프리터가 종료될 수 있습니다.

사용자는 일반적으로 인터럽트 키(보통 Ctrl-C)를 누르면 애플리케이션이 종료되기를 기대하므로 KeyboardInterrupt를 옮겼습니다. 지나치게 광범위한 except 절을 사용하면 기대한 동작이 일어나지 않습니다.

SystemExit도 비슷한 이유로 옮겼습니다. 이 예외는 sys.exit()가 호출될 때 발생하므로, 일반적으로 인터프리터가 종료될 수 있어야 합니다. 안타깝게도 지나치게 광범위한 except 절은 명시적으로 요청한 종료가 일어나지 못하게 막을 수 있습니다.

사람들이 대부분의 경우 Exception 예외를 잡도록 하려면 문서와 튜토리얼의 여러 부분을 갱신하여 프로그래머가 사용하려는 것이 Exception 예외여야 한다고 강력히 권해야 합니다. KeyboardInterrupt와 SystemExit는 거의 항상 상위로 전파될 수 있어야 하므로, 맨 except 절을 사용하거나 BaseException을 직접 잡는 것은 권장하지 않아야 합니다.

전환 계획

Python의 의미 체계를 변경하자는 제안이므로 전환 계획이 필요합니다. 목표는 2.x 코드가 매끄럽게 전환되도록 하면서 최종적으로 Python 3.0에서 새로운 의미 체계를 사용하는 것입니다. 이 계획에서 언급한 모든 폐지 예고는 처음 폐지 예고된 버전의 다음 버전부터 해당 의미 체계의 제거로 이어집니다.

다음은 2.x 시리즈에 구현된 BaseException입니다:

class BaseException(object):

    """Superclass representing the base of the exception hierarchy.

    The __getitem__ method is provided for backwards-compatibility
    and will be deprecated at some point.  The 'message' attribute
    is also deprecated.

    """

    def __init__(self, *args):
        self.args = args

    def __str__(self):
        return str(self.args[0]
                   if len(self.args) <= 1
                   else self.args)

    def __repr__(self):
        func_args = repr(self.args) if self.args else "()"
        return self.__class__.__name__ + func_args

    def __getitem__(self, index):
        """Index into arguments passed in during instantiation.

        Provided for backwards-compatibility and will be
        deprecated.

        """
        return self.args[index]

    def _get_message(self):
        """Method for 'message' property."""
        warnings.warn("the 'message' attribute has been deprecated "
                        "since Python 2.6")
        return self.args[0] if len(args) == 1 else ''

    message = property(_get_message,
                        doc="access the 'message' attribute; "
                            "deprecated and provided only for "
                            "backwards-compatibility")

Python 2.9에서 기능을 폐지 예고하는 것은 선택 사항입니다. Python 2.9(2.x 시리즈의 마지막 버전으로 예정됨)가 3.0에 포함되지 않을 기능을 적극적으로 폐지 예고할지 현재로서는 알 수 없기 때문입니다. 2.9와 3.0 사이에 차이가 너무 커서 2.9가 경고 측면에서 지나치게 “시끄러워질” 수 있으므로, 2.9에서는 폐지 예고 경고를 전혀 사용하지 않을 수도 있습니다. 따라서 Python 2.9에 대해 제안된 폐지 예고 경고는 해당 버전의 개발이 시작될 때 다시 검토하여 여전히 필요한지 결정할 것입니다.

  • Python 2.5 [완료]
    • 모든 표준 예외를 새 스타일 클래스로 전환 [완료]
    • BaseException 도입 [완료]
    • Exception, KeyboardInterrupt, SystemExit 예외는 BaseException을 상속합니다 [완료]
    • 문자열 예외 발생을 폐지 예고 [완료]
  • Python 2.6 [완료]
    • 문자열 예외 잡기를 폐지 예고 [완료]
    • message 속성을 폐지 예고 (Retracted Ideas 참고) [완료]
  • Python 2.7 [완료]
    • BaseException을 상속하지 않는 예외 발생을 폐지 예고
  • Python 3.0 [완료]
    • 위에서 폐지 예고한 모든 것을 제거:
      • 문자열 예외(발생과 잡기 모두) [완료]
      • 모든 예외는 BaseException을 상속해야 합니다 [완료]
      • __getitem__, message 제거 [완료]

철회된 아이디어

Python 2.5에서 구현되었던 이 PEP의 이전 버전에는 BaseException의 ‘message’ 속성이 포함되어 있었습니다. 그 목적은 BaseException이 단일 인자만 받도록 하는 전환을 시작하는 것이었습니다. 이는 인터페이스를 엄격하게 하고, 예외와 함께 임의의 정보를 전달할 때 모든 것을 args에 욱여넣는 대신 서브클래스의 속성을 사용하도록 강제하기 위한 것이었습니다.

안타깝게도 PyCon 2007 스프린트 [2]에서 Python 3.0의 args 속성 제거를 구현하던 중, 이 전환이 특히 C 확장 모듈에 매우 고통스럽다는 사실이 밝혀졌습니다. 그래서 Python 2.6에서 message 속성을 폐지 예정으로 지정하고(Python 2.7과 Python 3.0에서는 제거하고), Python 3.0에서는 BaseException의 다중 인자 지원을 제거하고 단일 인자만 받도록 하는 더 장기적인 전환 전략을 검토하는 편이 낫다고 결정되었습니다. 따라서 message의 도입과 args의 기존 폐지 예정 지정은 철회되었습니다.

참고 문헌