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?
Your super
invocation is wrong: self
should not be supplied again, it's already injected by super
. This way, file_error.args[0] is file_error
because you pass self
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:
super(FileError, self).__init__(filename, *a, **k)
The reason for the infinite recursion: First off, only object.__str__
delegates to __repr__
; BaseException
defines both __str__
and __repr__
separately, so str()
of an exception calls that overload, not your __repr__
. BaseException.__str__
usually prints the args tuple (which would use repr
), though when it contains a single argument, it prints the str()
of that single argument.
This invokes BaseException.__str__
again, and so on. Fix #2 prevents this cycle by not entering BaseException.__str__
in the first place, instead using your __repr__
which does not touch the args tuple at all.
This line is incorrect:
super( FileError, self ).__init__( self, *a, **k )
You need to pass self
in super()
but not again as argument to __init__
. So it needs to be:
super( FileError, self ).__init__( *a, **k )
Don't pass self
to __init__
as the first argument. That's causing the recursion.
It should be:
super( FileError, self ).__init__( filename, *a, **k )
The recursion is caused because
>>> print Exception("Abc")
Abc
Exception
prints the first argument. So when you initialize the base class of FileError
i.e. Exception
with self
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.
来源:https://stackoverflow.com/questions/21392135/python-maximum-recursion-depth-exceeded-when-printing-custom-exception