Proper way to declare custom exceptions in modern Python?

后端 未结 11 1646
栀梦
栀梦 2020-11-22 08:04

What\'s the proper way to declare custom exception classes in modern Python? My primary goal is to follow whatever standard other exception classes have, so that (for instan

相关标签:
11条回答
  • 2020-11-22 08:10

    As of Python 3.8 (2018, https://docs.python.org/dev/whatsnew/3.8.html), the recommended method is still:

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

    Please don't forget to document, why a custom exception is neccessary!

    If you need to, this is the way to go for exceptions with more data:

    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
    

    and fetch them like:

    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 is important to make it pickle-able. Before dumping it, you have to call error.__reduce__(). Loading will work as expected.

    You maybe should investigate in finding a solution using pythons return statement if you need much data to be transferred to some outer structure. This seems to be clearer/more pythonic to me. Advanced exceptions are heavily used in Java, which can sometimes be annoying, when using a framework and having to catch all possible errors.

    0 讨论(0)
  • 2020-11-22 08:16

    Maybe I missed the question, but why not:

    class MyException(Exception):
        pass
    

    Edit: to override something (or pass extra args), do this:

    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
    

    That way you could pass dict of error messages to the second param, and get to it later with e.errors


    Python 3 Update: In Python 3+, you can use this slightly more compact use of 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
    
    0 讨论(0)
  • 2020-11-22 08:18

    With modern Python Exceptions, you don't need to abuse .message, or override .__str__() or .__repr__() or any of it. If all you want is an informative message when your exception is raised, do this:

    class MyException(Exception):
        pass
    
    raise MyException("My hovercraft is full of eels")
    

    That will give a traceback ending with MyException: My hovercraft is full of eels.

    If you want more flexibility from the exception, you could pass a dictionary as the argument:

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

    However, to get at those details in an except block is a bit more complicated. The details are stored in the args attribute, which is a list. You would need to do something like this:

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

    It is still possible to pass in multiple items to the exception and access them via tuple indexes, but this is highly discouraged (and was even intended for deprecation a while back). If you do need more than a single piece of information and the above method is not sufficient for you, then you should subclass Exception as described in the tutorial.

    class MyError(Exception):
        def __init__(self, message, animal):
            self.message = message
            self.animal = animal
        def __str__(self):
            return self.message
    
    0 讨论(0)
  • 2020-11-22 08:18

    A really simple approach:

    class CustomError(Exception):
        pass
    
    raise CustomError("Hmm, seems like this was custom coded...")
    

    Or, have the error raise without printing __main__ (may look cleaner and neater):

    class CustomError(Exception):
        __module__ = Exception.__module__
    
    raise CustomError("Improved CustomError!")
    
    0 讨论(0)
  • 2020-11-22 08:20

    Try this Example

    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")
    
    0 讨论(0)
  • 2020-11-22 08:22

    You should override __repr__ or __unicode__ methods instead of using message, the args you provide when you construct the exception will be in the args attribute of the exception object.

    0 讨论(0)
提交回复
热议问题