[Final] PEP 352 - Required Superclass for Exceptions

원문 링크: PEP 352 - Required Superclass for Exceptions

상태: Final 유형: Standards Track 작성일: 27-Oct-2005

PEP 352 – 예외를 위한 필수 슈퍼클래스 (Required Superclass for Exceptions)

  • 작성자: Brett Cannon, Guido van Rossum
  • 상태: Final (최종)
  • 유형: Standards Track (표준 트랙)
  • 작성일: 2005년 10월 27일
  • Python 버전: 2.5
  • 번역 목표: 이 PEP의 제안 내용, 도입 배경, 그리고 실제 Python 사용에 미치는 영향을 한국어 사용자가 명확하게 이해할 수 있도록 돕습니다.

요약 (Abstract)

Python 2.4 이하 버전에서는 어떤 (클래식) 클래스든 예외로 발생시킬 수 있었습니다. Python 2.5에서는 새로운 스타일 클래스(new-style classes)도 허용할 계획이었는데, 이는 문제를 더욱 악화시킬 수 있었습니다. 즉, 모든 클래스(또는 인스턴스)를 예외로 발생시킬 수 있다는 의미가 됩니다. 이는 예외의 인터페이스에 대한 어떤 보장도 할 수 없게 만들었기 때문에 문제였습니다. 이 PEP는 모든 예외로 발생하는 객체가 반드시 상속해야 하는 새로운 슈퍼클래스(superclass)를 도입할 것을 제안합니다. 이러한 제한을 두면 예외를 위한 표준 인터페이스가 존재하게 되어 신뢰할 수 있게 됩니다. 또한, 모든 예외가 준수해야 할 알려진 계층 구조를 갖게 됩니다.

특정 인터페이스에 특정 기본 클래스를 요구하는 것이 Python답지 않다고 반박할 수도 있습니다. 하지만 예외의 경우, 충분히 합리적인 이유가 있습니다 (이는 python-dev 커뮤니티에서 일반적으로 동의되었습니다): 계층 구조를 요구하면 except BaseException: 대신 except *: 와 같이 작성하는 것이 가능해져서 모든 예외를 명시적으로 잡으려는 코드에 도움이 됩니다.

예외를 위한 새로운 슈퍼클래스를 도입하는 것은 예외 계층 구조를 더 좋게 재구성할 기회도 제공합니다. 현재 상태에서는 내장(built-in) 네임스페이스에 있는 모든 예외는 Exception을 상속합니다. 이는 KeyboardInterruptSystemExit라는 두 가지 예외를 포함하는데, 이들은 종종 애플리케이션의 예외 처리에서 제외되어야 할 필요가 있다는 점에서 문제가 됩니다. 일반적으로 트레이스백(traceback) 없이 인터프리터를 종료하는 기본 동작이 애플리케이션이 수행할 수 있는 어떤 동작보다 더 바람직하기 때문입니다 (Python의 대화형 명령 루프를 에뮬레이트하는 애플리케이션은 예외일 수 있습니다). 이 두 예외가 Exception 대신 공통 슈퍼클래스로부터 상속받도록 변경하면, 사람들이 과도하게 넓지 않은 except 절을 작성하여 전파되어야 할 예외를 잡지 않도록 쉽게 할 수 있습니다.

이 PEP는 PEP 348의 이전 작업을 기반으로 합니다.

공통 슈퍼클래스 요구 (Requiring a Common Superclass)

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

class BaseException(object):
    """예외 계층 구조의 기반을 나타내는 슈퍼클래스입니다.
    생성자에 전달된 모든 인수를 포함하는 'args' 속성을 제공합니다.
    하지만 권장되는 방식은 단일 문자열 인수만 생성자에 전달하는 것입니다.
    """
    def __init__(self, *args):
        self.args = args

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

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

하위 호환성(backwards-compatibility)을 위해 args에 전달될 수 있는 내용에는 제한이 없습니다. 하지만 실제로는 단일 문자열 인수만 사용해야 합니다. 이렇게 하면 예외의 문자열 표현이 사람이 읽을 수 있는 유용한 메시지가 됩니다. 이것이 __str__ 메서드가 길이가 1인 args 값을 특별 처리하는 이유입니다. 프로그래밍 정보(예: 오류 코드 번호)는 서브클래스에 별도의 속성으로 저장되어야 합니다.

raise 문은 전달되는 모든 객체가 BaseException을 상속해야 하도록 변경될 것입니다. 이는 모든 예외가 BaseException을 기반으로 하는 단일 계층 구조 내에 있도록 보장합니다. 또한 BaseException으로부터 상속되는 기본 인터페이스를 보장합니다. raise에 대한 변경 사항은 Python 3.0부터 적용될 것입니다 (아래 “Transition Plan” 참조).

BaseException이 예외 계층 구조의 루트(root)가 되면서, Exception은 이제 BaseException으로부터 상속받게 됩니다.

예외 계층 구조 변경 (Exception Hierarchy Changes)

예외 계층 구조가 이제 기본적인 루트를 가지게 되어 더욱 중요해졌으므로, 기존 계층 구조의 변경이 필요합니다. 현재로서는 오류를 알리고 인터프리터가 종료되어서는 안 된다고 의미하는 모든 예외를 잡으려면, except 절에 두 가지 예외를 제외한 모든 예외를 구체적으로 지정하거나, 두 예외를 따로 잡은 다음 다시 raise하고 다른 모든 예외는 bare except 절로 넘어가도록 해야 합니다:

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

이는 불필요하게 명시적입니다. 이 PEP는 KeyboardInterruptSystemExitBaseException으로부터 직접 상속받도록 이동할 것을 제안합니다.

  • BaseException
    • KeyboardInterrupt
    • SystemExit
    • Exception
      • (현재의 모든 다른 내장 예외들)

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

사용자들이 인터럽트 키(일반적으로 Ctrl-C)를 누르면 애플리케이션이 종료될 것으로 예상하기 때문에 KeyboardInterrupt가 이동되었습니다. 만약 사람들이 지나치게 넓은 except 절을 가지고 있다면 예상되는 동작이 발생하지 않습니다.

SystemExit도 유사한 이유로 이동되었습니다. sys.exit()가 호출될 때 예외가 발생하므로, 인터프리터는 일반적으로 종료될 수 있어야 합니다. 불행히도 지나치게 넓은 except 절은 명시적으로 요청된 종료가 발생하지 않도록 막을 수 있습니다.

사람들이 대부분의 경우 Exception을 잡도록 하기 위해, 문서와 튜토리얼의 여러 부분이 프로그래머들이 Exception을 사용하도록 강력하게 권장하도록 업데이트되어야 합니다. KeyboardInterruptSystemExit는 거의 항상 전파되도록 허용되어야 한다는 사실에 기반하여, bare except 절 또는 BaseException을 직접 잡는 것은 권장되지 않아야 합니다.

전환 계획 (Transition Plan)

Python에 대한 의미론적 변경이 제안되므로 전환 계획이 필요합니다. 목표는 Python 3.0에서 새로운 의미론이 사용되도록 하면서도 2.x 코드에 대한 원활한 전환을 제공하는 것입니다. 계획에 언급된 모든 사용 중단(deprecation)은 초기 사용 중단 이후 버전부터 의미론의 제거로 이어질 것입니다.

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

class BaseException(object):
    """예외 계층 구조의 기반을 나타내는 슈퍼클래스입니다.
    __getitem__ 메서드는 하위 호환성을 위해 제공되며 언젠가 사용 중단될 것입니다.
    'message' 속성도 사용 중단됩니다.
    """
    def __init__(self, *args):
        self.args = args

    def __str__(self):
        return str(self.args 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):
        """인스턴스화 시 전달된 인수에 대한 인덱스입니다.
        하위 호환성을 위해 제공되며 사용 중단될 것입니다.
        """
        return self.args[index]

    def _get_message(self):
        """'message' 속성을 위한 메서드입니다."""
        warnings.warn("the 'message' attribute has been deprecated "
                      "since Python 2.6")
        return self.args if len(self.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 [완료]

  • 모든 표준 예외가 new-style 클래스가 됨 [완료]
  • BaseException 도입 [완료]
  • Exception, KeyboardInterrupt, SystemExitBaseException으로부터 상속받음 [완료]
  • 문자열 예외(raising string exceptions) 사용 중단 [완료]

Python 2.6 [완료]

  • 문자열 예외(catching string exceptions) 잡기 사용 중단 [완료]
  • message 속성 사용 중단 (Retracted Ideas 참조) [완료]

Python 2.7 [완료]

  • BaseException을 상속하지 않는 예외 발생 사용 중단

Python 3.0 [완료]

  • 위에서 사용 중단된 모든 것 제거:
    • 문자열 예외 (발생 및 잡기 모두) [완료]
    • 모든 예외는 BaseException을 상속해야 함 [완료]
    • __getitem__, message 제거 [완료]

철회된 아이디어 (Retracted Ideas)

Python 2.5에 구현되었던 이 PEP의 이전 버전에는 BaseException에 ‘message’ 속성이 포함되어 있었습니다. 그 목적은 BaseException이 단일 인수만 받도록 전환을 시작하는 것이었습니다. 이는 인터페이스를 강화하고 사람들이 예외와 함께 임의의 정보를 args에 모두 밀어넣는 대신 서브클래스의 속성에 저장하도록 강제하기 위함이었습니다.

불행히도, PyCon 2007 스프린트에서 Python 3.0의 args 속성 제거를 구현하는 동안, 이 전환이 매우 고통스럽다는 것이 발견되었습니다. 특히 C 확장 모듈의 경우 더욱 그러했습니다. message 속성을 Python 2.6에서 사용 중단하고 (Python 2.7 및 Python 3.0에서 제거) Python 3.0에서 BaseException의 다중 인수 지원을 제거하고 단일 인수를 받는 것을 선호하는 장기적인 전환 전략을 고려하는 것이 더 낫다고 결정되었습니다. 따라서 message의 도입과 args의 원래 사용 중단은 철회되었습니다.

참고 자료 (References)

이 문서는 퍼블릭 도메인에 공개되었습니다.


PEP 352 – 예외를 위한 필수 슈퍼클래스 (Required Superclass for Exceptions)

  • 작성자: Brett Cannon, Guido van Rossum
  • 상태: Final (최종)
  • 유형: Standards Track (표준 트랙)
  • 작성일: 2005년 10월 27일
  • Python 버전: 2.5
  • 번역 목표: 이 PEP의 제안 내용, 도입 배경, 그리고 실제 Python 사용에 미치는 영향을 한국어 사용자가 명확하게 이해할 수 있도록 돕습니다.

요약 (Abstract)

Python 2.4 이하 버전에서는 어떤 (클래식) 클래스든 예외로 발생시킬 수 있었습니다. Python 2.5에서는 새로운 스타일 클래스(new-style classes)도 허용할 계획이었는데, 이는 문제를 더욱 악화시킬 수 있었습니다. 즉, 모든 클래스(또는 인스턴스)를 예외로 발생시킬 수 있다는 의미가 됩니다. 이는 예외의 인터페이스에 대한 어떤 보장도 할 수 없게 만들었기 때문에 문제였습니다. 이 PEP는 모든 예외로 발생하는 객체가 반드시 상속해야 하는 새로운 슈퍼클래스(superclass)를 도입할 것을 제안합니다. 이러한 제한을 두면 예외를 위한 표준 인터페이스가 존재하게 되어 신뢰할 수 있게 됩니다. 또한, 모든 예외가 준수해야 할 알려진 계층 구조를 갖게 됩니다.

특정 인터페이스에 특정 기본 클래스를 요구하는 것이 Python답지 않다고 반박할 수도 있습니다. 하지만 예외의 경우, 충분히 합리적인 이유가 있습니다 (이는 python-dev 커뮤니티에서 일반적으로 동의되었습니다): 계층 구조를 요구하면 except BaseException: 대신 except *: 와 같이 작성하는 것이 가능해져서 모든 예외를 명시적으로 잡으려는 코드에 도움이 됩니다.

예외를 위한 새로운 슈퍼클래스를 도입하는 것은 예외 계층 구조를 더 좋게 재구성할 기회도 제공합니다. 현재 상태에서는 내장(built-in) 네임스페이스에 있는 모든 예외는 Exception을 상속합니다. 이는 KeyboardInterruptSystemExit라는 두 가지 예외를 포함하는데, 이들은 종종 애플리케이션의 예외 처리에서 제외되어야 할 필요가 있다는 점에서 문제가 됩니다. 일반적으로 트레이스백(traceback) 없이 인터프리터를 종료하는 기본 동작이 애플리케이션이 수행할 수 있는 어떤 동작보다 더 바람직하기 때문입니다 (Python의 대화형 명령 루프를 에뮬레이트하는 애플리케이션은 예외일 수 있습니다). 이 두 예외가 Exception 대신 공통 슈퍼클래스로부터 상속받도록 변경하면, 사람들이 과도하게 넓지 않은 except 절을 작성하여 전파되어야 할 예외를 잡지 않도록 쉽게 할 수 있습니다.

이 PEP는 PEP 348의 이전 작업을 기반으로 합니다.

공통 슈퍼클래스 요구 (Requiring a Common Superclass)

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

class BaseException(object):
    """예외 계층 구조의 기반을 나타내는 슈퍼클래스입니다.
    생성자에 전달된 모든 인수를 포함하는 'args' 속성을 제공합니다.
    하지만 권장되는 방식은 단일 문자열 인수만 생성자에 전달하는 것입니다.
    """
    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))

하위 호환성(backwards-compatibility)을 위해 args에 전달될 수 있는 내용에는 제한이 없습니다. 하지만 실제로는 단일 문자열 인수만 사용해야 합니다. 이렇게 하면 예외의 문자열 표현이 사람이 읽을 수 있는 유용한 메시지가 됩니다. 이것이 __str__ 메서드가 길이가 1인 args 값을 특별 처리하는 이유입니다. 프로그래밍 정보(예: 오류 코드 번호)는 서브클래스에 별도의 속성으로 저장되어야 합니다.

raise 문은 전달되는 모든 객체가 BaseException을 상속해야 하도록 변경될 것입니다. 이는 모든 예외가 BaseException을 기반으로 하는 단일 계층 구조 내에 있도록 보장합니다. 또한 BaseException으로부터 상속되는 기본 인터페이스를 보장합니다. raise에 대한 변경 사항은 Python 3.0부터 적용될 것입니다 (아래 “Transition Plan” 참조).

BaseException이 예외 계층 구조의 루트(root)가 되면서, Exception은 이제 BaseException으로부터 상속받게 됩니다.

예외 계층 구조 변경 (Exception Hierarchy Changes)

예외 계층 구조가 이제 기본적인 루트를 가지게 되어 더욱 중요해졌으므로, 기존 계층 구조의 변경이 필요합니다. 현재로서는 오류를 알리고 인터프리터가 종료되어서는 안 된다고 의미하는 모든 예외를 잡으려면, except 절에 두 가지 예외를 제외한 모든 예외를 구체적으로 지정하거나, 두 예외를 따로 잡은 다음 다시 raise하고 다른 모든 예외는 bare except 절로 넘어가도록 해야 합니다:

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

이는 불필요하게 명시적입니다. 이 PEP는 KeyboardInterruptSystemExitBaseException으로부터 직접 상속받도록 이동할 것을 제안합니다.

  • BaseException
    • KeyboardInterrupt
    • SystemExit
    • Exception
      • (현재의 모든 다른 내장 예외들)

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

사용자들이 인터럽트 키(일반적으로 Ctrl-C)를 누르면 애플리케이션이 종료될 것으로 예상하기 때문에 KeyboardInterrupt가 이동되었습니다. 만약 사람들이 지나치게 넓은 except 절을 가지고 있다면 예상되는 동작이 발생하지 않습니다.

SystemExit도 유사한 이유로 이동되었습니다. sys.exit()가 호출될 때 예외가 발생하므로, 인터프리터는 일반적으로 종료될 수 있어야 합니다. 불행히도 지나치게 넓은 except 절은 명시적으로 요청된 종료가 발생하지 않도록 막을 수 있습니다.

사람들이 대부분의 경우 Exception을 잡도록 하기 위해, 문서와 튜토리얼의 여러 부분이 프로그래머들이 Exception을 사용하도록 강력하게 권장하도록 업데이트되어야 합니다. KeyboardInterruptSystemExit는 거의 항상 전파되도록 허용되어야 한다는 사실에 기반하여, bare except 절 또는 BaseException을 직접 잡는 것은 권장되지 않아야 합니다.

전환 계획 (Transition Plan)

Python에 대한 의미론적 변경이 제안되므로 전환 계획이 필요합니다. 목표는 Python 3.0에서 새로운 의미론이 사용되도록 하면서도 2.x 코드에 대한 원활한 전환을 제공하는 것입니다. 계획에 언급된 모든 사용 중단(deprecation)은 초기 사용 중단 이후 버전부터 의미론의 제거로 이어질 것입니다.

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

class BaseException(object):
    """예외 계층 구조의 기반을 나타내는 슈퍼클래스입니다.
    __getitem__ 메서드는 하위 호환성을 위해 제공되며 언젠가 사용 중단될 것입니다.
    'message' 속성도 사용 중단됩니다.
    """
    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):
        """인스턴스화 시 전달된 인수에 대한 인덱스입니다.
        하위 호환성을 위해 제공되며 사용 중단될 것입니다.
        """
        return self.args[index]

    def _get_message(self):
        """'message' 속성을 위한 메서드입니다."""
        warnings.warn("the 'message' attribute has been deprecated "
                      "since Python 2.6")
        return self.args[0] if len(self.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 [완료]

  • 모든 표준 예외가 new-style 클래스가 됨 [완료]
  • BaseException 도입 [완료]
  • Exception, KeyboardInterrupt, SystemExitBaseException으로부터 상속받음 [완료]
  • 문자열 예외(raising string exceptions) 사용 중단 [완료]

Python 2.6 [완료]

  • 문자열 예외(catching string exceptions) 잡기 사용 중단 [완료]
  • message 속성 사용 중단 (Retracted Ideas 참조) [완료]

Python 2.7 [완료]

  • BaseException을 상속하지 않는 예외 발생 사용 중단

Python 3.0 [완료]

  • 위에서 사용 중단된 모든 것 제거:
    • 문자열 예외 (발생 및 잡기 모두) [완료]
    • 모든 예외는 BaseException을 상속해야 함 [완료]
    • __getitem__, message 제거 [완료]

철회된 아이디어 (Retracted Ideas)

Python 2.5에 구현되었던 이 PEP의 이전 버전에는 BaseException에 ‘message’ 속성이 포함되어 있었습니다. 그 목적은 BaseException이 단일 인수만 받도록 전환을 시작하는 것이었습니다. 이는 인터페이스를 강화하고 사람들이 예외와 함께 임의의 정보를 args에 모두 밀어넣는 대신 서브클래스의 속성에 저장하도록 강제하기 위함이었습니다.

불행히도, PyCon 2007 스프린트에서 Python 3.0의 args 속성 제거를 구현하는 동안, 이 전환이 매우 고통스럽다는 것이 발견되었습니다. 특히 C 확장 모듈의 경우 더욱 그러했습니다. message 속성을 Python 2.6에서 사용 중단하고 (Python 2.7 및 Python 3.0에서 제거) Python 3.0에서 BaseException의 다중 인수 지원을 제거하고 단일 인수를 받는 것을 선호하는 장기적인 전환 전략을 고려하는 것이 더 낫다고 결정되었습니다. 따라서 message의 도입과 args의 원래 사용 중단은 철회되었습니다.

참고 자료 (References)

이 문서는 퍼블릭 도메인에 공개되었습니다.

⚠️ 알림: 이 문서는 AI를 활용하여 번역되었으며, 기술적 정확성을 보장하지 않습니다. 정확한 내용은 반드시 원문을 확인하시기 바랍니다.

Comments