질문자 :Nelson
현대 Python에서 사용자 정의 예외 클래스를 선언하는 적절한 방법은 무엇입니까? 내 주요 목표는 다른 예외 클래스의 표준을 따르는 것입니다. 예를 들어 예외에 포함된 추가 문자열은 예외를 포착한 도구에 의해 출력됩니다.
"현대 Python"이란 Python 2.5에서 실행되지만 Python 2.6 및 Python 3.* 작업 방식에 '올바른' 것을 의미합니다. 그리고 "사용자 정의"란 오류의 원인에 대한 추가 데이터를 포함할 수 있는 예외 개체를 의미합니다. 문자열, 예외와 관련된 다른 임의 개체일 수도 있습니다.
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
무언가를 재정의하거나 추가 인수를 전달하려면 다음을 수행하십시오.
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
그렇게 하면 오류 메시지 사전을 두 번째 매개변수에 전달할 수 있고 나중에 e.errors
얻을 수 있습니다.
Python 2에서는 다음과 같이 약간 더 복잡한 형태의 super()
를 사용해야 합니다.
super(ValidationError, self).__init__(message)
gahooa최신 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
frnknstn "현대 Python에서 사용자 정의 예외를 선언하는 적절한 방법은 무엇입니까?"
예외가 실제로 더 구체적인 예외 유형이 아닌 한 괜찮습니다.
class MyException(Exception): pass
또는 더 나은(완벽할 수도 있음) pass
대신 독스트링을 제공합니다.
class MyException(Exception): """Raise for my specific kind of exception"""
예외 서브클래스 서브클래싱
문서에서
Exception
모든 기본 제공 비시스템 종료 예외는 이 클래스에서 파생됩니다. 모든 사용자 정의 예외도 이 클래스에서 파생되어야 합니다.
그 말 귀하의 예외는보다 구체적인 예외의 유형, 대신 일반의 예외 있다는 서브 클래스 인 경우 Exception
(그리고 그 결과는 여전히에서 파생하는 것이 될 것입니다 Exception
워드 프로세서 추천으로). 또한 최소한 docstring을 제공할 수 있습니다(그리고 pass
키워드를 사용하도록 강요되지 않음).
class MyAppValueError(ValueError): '''Raise when my specific value is wrong'''
__init__
를 사용하여 직접 만든 속성을 설정합니다. dict를 위치 인수로 전달하지 마십시오. 코드의 미래 사용자는 감사할 것입니다. 더 이상 사용되지 않는 메시지 속성을 사용하는 경우 이를 직접 할당하면 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
를 통해 초기화하는 방법이 아니며 message 속성에 액세스하면 DeprecationWarning
편집: 무언가를 재정의하거나 추가 인수를 전달하려면 다음을 수행하십시오.
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
그렇게하면 오류 메시지 사전을 두 번째 매개 변수에 전달할 수 있고 나중에 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'
Aaron Hall이상의 속성 대 한 (역 추적 생략)를 사용하는 경우 예외가 기본적으로 작동 방법 :
>>> 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
기본 튜플과 유사한 표현이 마음에 들지 않으면 다음과 같이 ExceptionTemplate
클래스에 __str__
메서드를 추가하면 됩니다.
# ... def __str__(self): return ': '.join(self.args)
그리고 당신은 가질 것입니다
>>> raise nastyerr('code is broken') NastyError: bad thing happened: code is broken
mykhalPython 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에서 많이 사용되며 프레임워크를 사용하고 가능한 모든 오류를 잡아야 할 때 때때로 성가실 수 있습니다.
fameman자신의 예외를 올바르게 정의하려면 따라야 하는 몇 가지 모범 사례가 있습니다.
Exception
에서 상속 하는 기본 클래스를 정의합니다. 이렇게 하면 프로젝트와 관련된 모든 예외를 쉽게 포착할 수 있습니다.
class MyProjectError(Exception): """A base class for MyProject exceptions."""
예외 클래스를 별도의 모듈(예: exceptions.py
)로 구성하는 것은 일반적으로 좋은 생각입니다.
특정 예외를 생성하려면 기본 예외 클래스를 서브클래싱합니다.
사용자 정의 예외에 추가 인수에 대한 지원을 추가하려면 가변 인수 수로 __init__()
기본 클래스의 __init__()
호출하여 위치 인수를 전달합니다( BaseException
/ Exception
임의의 수의 위치 인수를 예상함을 기억하십시오):
class CustomError(MyProjectError): def __init__(self, *args, **kwargs): super().__init__(*args) self.foo = kwargs.get('foo')
추가 인수로 이러한 예외를 발생시키려면 다음을 사용할 수 있습니다.
raise CustomError('Something bad happened', foo='foo')
이 디자인은 기본 예외 클래스의 인스턴스를 파생된 예외 클래스의 인스턴스로 바꿀 수 있기 때문에 Liskov 대체 원칙을 따릅니다. 또한 부모와 동일한 매개 변수를 사용하여 파생 클래스의 인스턴스를 만들 수 있습니다.
Eugene Yarmash__repr__
또는 __unicode__
메서드를 재정의해야 합니다. 예외를 구성할 때 제공하는 args
는 예외 개체의 args 속성에 있습니다.
M. Utku ALTINKAYA아주 좋은 기사 " Python 예외에 대한 최종 가이드 "를 참조하십시오. 기본 원칙은 다음과 같습니다.
- 항상 (적어도) 예외에서 상속합니다.
- 항상 하나의 인수로
BaseException.__init__
을 호출하십시오. - 라이브러리를 빌드할 때 Exception을 상속하는 기본 클래스를 정의합니다.
- 오류에 대한 세부정보를 제공합니다.
- 타당한 경우 기본 제공 예외 유형에서 상속합니다.
(모듈에서) 구성 및 예외 래핑에 대한 정보도 있습니다. 가이드를 읽는 것이 좋습니다.
Yaroslav Nikitenko아니요, "메시지"는 금지되지 않습니다. 지원 중단되었습니다. 응용 프로그램은 메시지를 사용하여 잘 작동합니다. 그러나 물론 사용 중단 오류를 제거하고 싶을 수도 있습니다.
응용 프로그램에 대한 사용자 지정 예외 클래스를 만들 때 많은 클래스가 예외에서 뿐만 아니라 ValueError 또는 이와 유사한 것과 같은 다른 클래스에서 하위 클래스를 생성합니다. 그런 다음 변수 사용에 적응해야 합니다.
그리고 애플리케이션에 많은 예외가 있는 경우 일반적으로 모든 예외에 대한 공통 사용자 정의 기본 클래스를 갖는 것이 좋습니다. 그러면 모듈 사용자가 다음을 수행할 수 있습니다.
try: ... except NelsonsExceptions: ...
그리고 그 경우에 __init__ and __str__
수행할 수 있으므로 모든 예외에 대해 이를 반복할 필요가 없습니다. 그러나 단순히 메시지가 아닌 다른 것으로 메시지 변수를 호출하는 것은 트릭을 수행합니다.
어떤 경우든 Exception 자체가 하는 것과 다른 것을 하는 경우 __init__ or __str__
지원 중단인 경우 둘 다 필요하거나 오류가 발생하기 때문입니다. 클래스당 필요한 추가 코드가 그렇게 많지는 않습니다. ;)
Lennart Regebro이 예를 시도
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")
omkaartg정말 간단한 접근 방식:
class CustomError(Exception): pass raise CustomError("Hmm, seems like this was custom coded...")
__main__
을 인쇄하지 않고 오류가 발생하도록 하십시오(더 깨끗하고 깔끔하게 보일 수 있음):
class CustomError(Exception): __module__ = Exception.__module__ raise CustomError("Improved CustomError!")
Macintosh Fan출처 : http:www.stackoverflow.com/questions/1319615/proper-way-to-declare-custom-exceptions-in-modern-python