The following code throws RuntimeError: maximum recursion depth exceeded while getting the str of an object
. I can resolve the infinite recursion in two different ways, but I don't understand why each fix works and thus don't know which to use, or if either are correct.
class FileError( Exception ):
def __init__( self, filename=None, *a, **k ):
#Fix 1: remove super
super( FileError, self ).__init__( self, *a, **k )
self.filename = filename
def __repr__( self ):
return "<{0} ({1})>".format( self.__class__.__name__, self.filename )
#Fix 2: explicitly define __str__
#__str__ = __repr__
print( FileError( "abc" ) )
If I remove super
, the code runs but doesn't print anything. This doesn't make sense since according to this post, Difference between __str__ and __repr__ in Python, omitting __str__
will call __repr__
but that doesn't seem to be happening here.
If I, instead, keep the call to super
and add __str__ = __repr__
, then I get the expected output and there is no recursion.
Can someone explain why the infinite recursion is present, why each change resolves the inifinte recursion, and why one fix might be preferred over the other?
Don't pass
self
to__init__
as the first argument. That's causing the recursion.It should be:
The recursion is caused because
Exception
prints the first argument. So when you initialize the base class ofFileError
i.e.Exception
withself
which inherits__str__
from it's parent which prints the first argument (hope you see the recursion in the statement).. hence you get the infinite recursion.__str__ = __repr__
overrides the inherited__str__
and mitigates the infinite recursion.Your
super
invocation is wrong:self
should not be supplied again, it's already injected bysuper
. This way,file_error.args[0] is file_error
because you passself
as an extra argument to the exception constructor. This should make it obvious why fix #1 (removing the super call altogether) helps, but of course the best fix is to pass the right arguments:The reason for the infinite recursion: First off, only
object.__str__
delegates to__repr__
;BaseException
defines both__str__
and__repr__
separately, sostr()
of an exception calls that overload, not your__repr__
.BaseException.__str__
usually prints the args tuple (which would userepr
), though when it contains a single argument, it prints thestr()
of that single argument.This invokes
BaseException.__str__
again, and so on. Fix #2 prevents this cycle by not enteringBaseException.__str__
in the first place, instead using your__repr__
which does not touch the args tuple at all.This line is incorrect:
You need to pass
self
insuper()
but not again as argument to__init__
. So it needs to be: