Python: Maximum recursion depth exceeded when prin

2020-08-25 06:53发布

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?

3条回答
该账号已被封号
2楼-- · 2020-08-25 07:30

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.

查看更多
狗以群分
3楼-- · 2020-08-25 07:39

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.

查看更多
走好不送
4楼-- · 2020-08-25 07:46

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 )
查看更多
登录 后发表回答