Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 675
Ray Avatar asked Oct 28 '25 05:10

Ray


1 Answers

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.

like image 75
Martijn Pieters Avatar answered Oct 29 '25 19:10

Martijn Pieters