Python: Maximum recursion depth exceeded when printing custom exception

亡梦爱人 提交于 2020-01-02 01:46:29

问题


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?


回答1:


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.




回答2:


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 )



回答3:


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!