최신 Python에서 사용자 정의 예외를 선언하는 올바른 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/1319615

문제

최신 Python에서 사용자 정의 예외 클래스를 선언하는 적절한 방법은 무엇입니까?나의 주요 목표는 다른 예외 클래스의 표준을 따르는 것입니다. 예를 들어 예외에 포함된 추가 문자열은 예외를 포착한 도구에 의해 인쇄됩니다.

"현대적인 Python"이란 Python 2.5에서 실행되지만 Python 2.6 및 Python 3.* 작업 방식에서는 '올바른' 것을 의미합니다."사용자 정의"란 오류 원인에 대한 추가 데이터를 포함할 수 있는 Exception 객체를 의미합니다.문자열일 수도 있고, 예외와 관련된 다른 임의의 개체일 수도 있습니다.

Python 2.6.2에서 다음과 같은 지원 중단 경고가 표시되어 당황했습니다.

>>> class MyError(Exception):
...     def __init__(self, message):
...         self.message = message
... 
>>> MyError("foo")
_sandbox.py:3: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6

미친거 같은데 BaseException 이름이 지정된 속성에 대해 특별한 의미를 갖습니다. message.나는에서 수집 PEP-352 그 속성은 2.5에서 특별한 의미를 가졌고 그들은 더 이상 사용하지 않으려고 했기 때문에 그 이름(그리고 그 이름만)이 이제 금지된 것 같아요?윽.

나도 그걸 어렴풋이 알고 있어 Exception 마법의 매개변수가 있습니다 args, 그러나 나는 그것을 사용하는 방법을 결코 알지 못했습니다.또한 이것이 앞으로 일을 진행하는 올바른 방법인지도 확신하지 못합니다.내가 온라인에서 찾은 많은 토론에서는 Python 3에서 args를 없애려고 한다고 제안했습니다.

업데이트:두 가지 답변이 재정의를 제안했습니다 __init__, 그리고 __str__/__unicode__/__repr__.타이핑을 많이 하는 것 같은데, 꼭 필요한가요?

도움이 되었습니까?

해결책

어쩌면 나는 질문을 놓쳤을 것입니다. 그러나 왜 :

class MyException(Exception):
    pass

편집하다: 무언가를 무시하거나 추가 args를 통과하려면 다음을 수행하십시오.

class ValidationError(Exception):
    def __init__(self, message, errors):

        # Call the base class constructor with the parameters it needs
        super(ValidationError, self).__init__(message)

        # Now for your custom code...
        self.errors = errors

그렇게하면 오류 메시지의 DITT를 두 번째 매개 변수로 전달하고 나중에 얻을 수 있습니다. e.errors


Python 3 업데이트 : Python 3+에서는이 약간 더 컴팩트 한 사용을 사용할 수 있습니다. super():

class ValidationError(Exception):
    def __init__(self, message, errors):

        # Call the base class constructor with the parameters it needs
        super().__init__(message)

        # Now for your custom code...
        self.errors = errors

다른 팁

현대적인 Python 예외를 사용하면 남용 할 필요가 없습니다. .message, 또는 재정의 .__str__() 또는 .__repr__() 또는 그 중 하나. 예외가 제기 될 때 유익한 메시지라면 원하는 것은 다음을 수행하십시오.

class MyException(Exception):
    pass

raise MyException("My hovercraft is full of eels")

그것은 트레이스 백 끝을 줄 것입니다 MyException: My hovercraft is full of eels.

예외에서 더 많은 유연성을 원한다면 사전을 인수로 전달할 수 있습니다.

raise MyException({"message":"My hovercraft is full of animals", "animal":"eels"})

그러나 그 세부 사항을 통해 except 블록은 조금 더 복잡합니다. 세부 사항은 다음에 저장됩니다 args 속성, 이것은 목록입니다. 다음과 같은 일을해야합니다.

try:
    raise MyException({"message":"My hovercraft is full of animals", "animal":"eels"})
except MyException as e:
    details = e.args[0]
    print(details["animal"])

여전히 여러 항목을 예외로 전달하여 튜플 인덱스를 통해 액세스 할 수 있지만 이것은 다음과 같습니다. 매우 낙담했습니다 (그리고 잠시 동안 감가 상각을위한 것이 었습니다). 단일 정보 이상의 정보가 필요하고 위의 방법만으로는 충분하지 않은 경우 서브 클래스를해야합니다. Exception 에 설명 된대로 지도 시간.

class MyError(Exception):
    def __init__(self, message, animal):
        self.message = message
        self.animal = animal
    def __str__(self):
        return self.message

"현대 파이썬에서 맞춤형 예외를 선언하는 적절한 방법?"

예외가 실제로 더 구체적인 예외가 아닌 한 괜찮습니다.

class MyException(Exception):
    pass

또는 더 나은 (아마도 완벽 함) 대신 pass 닥터 스트링을 제공하십시오.

class MyException(Exception):
    """Raise for my specific kind of exception"""

서브 클래스 예외 서브 클래스

로부터 문서

Exception

모든 내장, 비 시스템 노출 예외는이 클래스에서 파생됩니다. 모든 사용자 정의 예외는이 클래스에서 파생되어야합니다.

그것은 의미합니다 만약에 예외는보다 구체적인 예외 유형이며 일반 대신 예외는 서브 클래스입니다. Exception (결과는 여전히 당신이 Exception 문서가 권장하는대로). 또한 최소한 문서를 제공 할 수 있습니다 (그리고 사용하지 않아도됩니다. pass 예어):

class MyAppValueError(ValueError):
    '''Raise when my specific value is wrong'''

사용자 정의로 자신을 만드는 속성을 설정하십시오 __init__. 포지션 논쟁으로서의 독점 전달을 피하면 코드의 향후 사용자가 감사 할 것입니다. 감가 상각 된 메시지 속성을 사용하면 직접 할당하면 피할 수 있습니다. DeprecationWarning:

class MyAppValueError(ValueError):
    '''Raise when a specific subset of values in context of app is wrong'''
    def __init__(self, message, foo, *args):
        self.message = message # without this you may get DeprecationWarning
        # Special attribute you desire with your Error, 
        # perhaps the value that caused the error?:
        self.foo = foo         
        # allow users initialize misc. arguments as any other builtin Error
        super(MyAppValueError, self).__init__(message, foo, *args) 

실제로 자신의 쓸 필요가 없습니다 __str__ 또는 __repr__. 내장 된 것들은 매우 친절하고 당신 협력 상속 당신이 그것을 사용하도록합니다.

최고 답변에 대한 비판

어쩌면 나는 질문을 놓쳤을 것입니다. 그러나 왜 :

class MyException(Exception):
    pass

다시 말하지만, 위의 문제는 그것을 잡기 위해서는 구체적으로 이름을 지정해야한다는 것입니다 (다른 곳에서 생성 된 경우 가져 오기) 예외 (그러나 모든 유형의 예외를 처리 할 준비가되지 않았을 것입니다. 그리고 당신은 당신이 처리 할 준비가 된 예외 만 포착해야합니다). 아래와 비슷한 비판이지만, 또한 그것은이를 통해 초기화하는 방법이 아닙니다. super, 그리고 당신은 얻을 수 있습니다 DeprecationWarning 메시지 속성에 액세스하는 경우 :

편집 : 무언가를 무시하거나 추가 args를 통과하려면 다음을 수행하십시오.

class ValidationError(Exception):
    def __init__(self, message, errors):

        # Call the base class constructor with the parameters it needs
        super(ValidationError, self).__init__(message)

        # Now for your custom code...
        self.errors = errors

그렇게하면 오류 메시지의 DITT를 두 번째 매개 변수로 전달하고 나중에 E.Errors와 함께 얻을 수 있습니다.

또한 정확히 두 개의 인수가 통과되어야합니다 ( self.) 그 이상도 이하도 아닌. 그것은 미래의 사용자가 이해하지 못하는 흥미로운 제약입니다.

직접적으로 - 위반합니다 Liskov 대체 가능성.

두 오류를 모두 보여 줄게.

>>> ValidationError('foo', 'bar', 'baz').message

Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    ValidationError('foo', 'bar', 'baz').message
TypeError: __init__() takes exactly 3 arguments (4 given)

>>> ValidationError('foo', 'bar').message
__main__:1: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
'foo'

비교 :

>>> MyAppValueError('foo', 'FOO', 'bar').message
'foo'

예외가 기본적으로 어떻게 작동하는지 확인하십시오 vs 더 많은 속성이 사용됩니다 (추적 백이 생략 됨) :

>>> raise Exception('bad thing happened')
Exception: bad thing happened

>>> raise Exception('bad thing happened', 'code is broken')
Exception: ('bad thing happened', 'code is broken')

그래서 당신은 일종의 것을 갖고 싶을 수도 있습니다.예외 템플릿", 호환 가능한 방식으로 예외 자체로 작업 :

>>> nastyerr = NastyError('bad thing happened')
>>> raise nastyerr
NastyError: bad thing happened

>>> raise nastyerr()
NastyError: bad thing happened

>>> raise nastyerr('code is broken')
NastyError: ('bad thing happened', 'code is broken')

이 서브 클래스로 쉽게 수행 할 수 있습니다

class ExceptionTemplate(Exception):
    def __call__(self, *args):
        return self.__class__(*(self.args + args))
# ...
class NastyError(ExceptionTemplate): pass

그리고 기본 튜플과 같은 표현이 마음에 들지 않으면 추가하십시오. __str__ 방법에 대한 방법 ExceptionTemplate 클래스, 좋아요 :

    # ...
    def __str__(self):
        return ': '.join(self.args)

그리고 당신은 가질 것입니다

>>> raise nastyerr('code is broken')
NastyError: bad thing happened: code is broken

당신은 무시해야합니다 __repr__ 또는 __unicode__ 메소드 메시지를 사용하는 대신 예외를 구성 할 때 제공하는 Args가 args 예외 객체의 속성.

파이썬 3.8부터 (2018, https://docs.python.org/dev/whatsnew/3.8.html) 권장되는 방법은 여전히 ​​다음과 같습니다.

class CustomExceptionName(Exception):
    """Exception raised when very uncommon things happen"""
    pass

사용자 정의 예외가 필요한 이유를 문서화하는 것을 잊지 마십시오!

필요한 경우 더 많은 데이터를 사용하여 예외를 적용하는 방법은 다음과 같습니다.

class CustomExceptionName(Exception):
    """Still an exception raised when uncommon things happen"""
    def __init__(self, message, payload=None):
        self.message = message
        self.payload = payload # you could add more args
    def __str__(self):
        return str(self.message) # __str__() obviously expects a string to be returned, so make sure not to send any other data types

다음과 같이 가져옵니다.

try:
    raise CustomExceptionName("Very bad mistake.", "Forgot upgrading from Python 1")
except CustomExceptionName as error:
    print(str(error)) # Very bad mistake
    print("Detail: {}".format(error.payload)) # Detail: Forgot upgrading from Python 1

payload=None 피클 가능하게 만드는 것이 중요합니다.버리기 전에 전화부터 해야지 error.__reduce__().로드는 예상대로 작동합니다.

파이썬을 사용하여 솔루션을 찾는 방법을 조사해야 할 수도 있습니다. return 외부 구조로 전송하기 위해 많은 데이터가 필요한 경우 명령문.이것은 나에게 더 명확하고 더 파이썬적인 것 같습니다.고급 예외는 Java에서 많이 사용되며, 이는 프레임워크를 사용하고 가능한 모든 오류를 포착해야 할 때 때때로 성가실 수 있습니다.

아니요, "메시지"는 금지되지 않습니다. 방금 더 이상 사용되지 않았습니다. 응용 프로그램은 메시지를 사용하여 제대로 작동합니다. 그러나 물론 감가 상각 오류를 제거하고 싶을 수도 있습니다.

애플리케이션에 대한 사용자 정의 예외 클래스를 만들 때 많은 클래스는 예외에서 서브 클래스가 아니라 ValueError 또는 이와 유사한 것과 같은 다른 것들로부터 하위 클래스를 제공합니다. 그런 다음 변수 사용에 적응해야합니다.

애플리케이션에 많은 예외가있는 경우 일반적으로 모듈 사용자가 수행 할 수 있도록 공통 사용자 정의 기본 클래스를 갖는 것이 좋습니다.

try:
    ...
except NelsonsExceptions:
    ...

그리고 그 경우 당신은 할 수 있습니다 __init__ and __str__ 거기에 필요 했으므로 모든 예외를 위해 반복 할 필요는 없습니다. 그러나 메시지 변수를 메시지보다 다른 것을 호출하는 것이 속임수를 만듭니다.

어쨌든, 당신은 만 필요합니다 __init__ or __str__ 예외 자체와 다른 일을한다면. 그리고 감가 상징이 있으면 둘 다 필요하거나 오류가 발생하기 때문입니다. 클래스 당 필요한 추가 코드가 아닙니다. ;)

이 예제를 시도하십시오

class InvalidInputError(Exception):
    def __init__(self, msg):
        self.msg = msg
    def __str__(self):
        return repr(self.msg)

inp = int(input("Enter a number between 1 to 10:"))
try:
    if type(inp) != int or inp not in list(range(1,11)):
        raise InvalidInputError
except InvalidInputError:
    print("Invalid input entered")

아주 좋은 기사보기 "파이썬 예외에 대한 결정적인 안내서". 기본 원칙은 다음과 같습니다.

  • 항상 (적어도) 예외에서 상속됩니다.
  • 항상 전화하십시오 BaseException.__init__ 하나의 주장만으로.
  • 라이브러리를 구축 할 때 예외에서 기본 클래스 상속을 정의하십시오.
  • 오류에 대한 세부 정보를 제공합니다.
  • 의미가있을 때 내장 예외 유형에서 상속됩니다.

모듈에서 구성 및 래핑 예외에 대한 정보도 있습니다. 가이드를 읽는 것이 좋습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top