Search code examples
pythonexceptionraise

Why "raise Exception" result in nothing?


If I have an expression of 1/0, obviously, it gets error:

try:
    1/0
except ZeroDivisionError as err:
    print(err)                      # this prints: division by zero

And the second try, replacing 1/0 by raise ZeroDivisionError...

try:
    raise ZeroDivisionError
except ZeroDivisionError as err:
    print(err)                      # this prints: (nothing)

It prints nothing. Isn't an exception came from raise, same as the a general expression?

Also, how to understand this difference more clearly?


Solution

  • All exceptions are a subclass of BaseException, therefore all built-in exceptions should have an args attribute.

    args:

    The tuple of arguments given to the exception constructor. Some built-in exceptions (like OSError) expect a certain number of arguments and assign a special meaning to the elements of this tuple, while others are usually called only with a single string giving an error message.

    The args tuple or string can be provided as the first argument when you raise the exception.

    try:
        raise ZeroDivisionError("error")
    except ZeroDivisionError as err:
        print(err)  # prints "error"
    

    The err from except Exception as err: is the Exception instance, when you print(err), you're actually calling the __str__ method of the Exception. Most Exception class's __str__ return's args, since it's the default behaviour of BaseException; __str__ will return otherwise if the exception class override BaseException's __str__.

    When you raised a plain ZeroDivisionError, you provided no args, and ZeroDivisionError does not have a custom __str__ method, therefore it printed args by default, a.k.a args = None.


    As for your question:

    Isn't an exception came from raise, same as the a general expression?

    Yes. They are the same.

    try:
        raise ZeroDivisionError("division by zero")
    except ZeroDivisionError as err:
        print(err)       
    

    This will output the same as 1/0.


    I went ahead and dig through the source code. // (integer division) and / (true division) have a slightly different error message. But basically they are defined as such:

    if (size_b == 0) {
            PyErr_SetString(PyExc_ZeroDivisionError,
                            "division by zero");
            return -1;
        }
    

    Whereas size_b is the divisor. As you can see, 1/0 or any division by zero raises a ZeroDivsionError with args set as "division by zero" or "integer division or modulo by zero" depending how you divide.