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.