Ray Ray - 4 months ago 18
Python Question

Implementation of `Exception.__str__()` in Python

I've never fully understood exception handling in Python (or any language to be honest). I was experimenting with custom exceptions, and found the following behaviour.

class MyError(Exception):
def __init__(self, anything):
pass

me = MyError("iiiiii")
print(me)


Output:

iiiiii


I assume that
print()
calls
Exception.__str__()
.

How does the base class
Exception
know to print
iiiiii
? The string
"iiiiii"
was passed to the constructor of
MyError
via the argument
anything
, but
anything
isn't stored anywhere in
MyError
at all!

Furthermore, the constructor of
MyError
does not call its superclass's (Exception's) constructor. So, how did
print(me)
print
iiiiii
?

Answer

In Python 3, the BaseException class has a __new__ method that stores the arguments in self.args:

>>> me.args
('iiiiii',)

You didn't override the __new__ method, only __init__. You'd need to override both to completely prevent from self.args to be set, as both implementations happily set that attribute:

>>> class MyError(Exception):
...     def __new__(cls, *args, **kw):
...         return super().__new__(cls)  # ignoring args and kwargs!
...     def __init__(self, *args, **kw):
...         super().__init__()           # again ignoring args and kwargs
...
>>> me = MyError("iiiiii")
>>> me
MyError()
>>> print(me)

>>> me.args
()

In Python 2, exceptions do not implement __new__ and your sample would not print anything. See issue #1692335 as to why the __new__ method was added; basically to avoid issues like yours where the __init__ method does not also call super().__init__().

Note that __init__ is not a constructor; the instance is already constructed by that time, by __new__. __init__ is merely the initialiser.