Python:打印自定义异常时超出最大递归深度

Posted

技术标签:

【中文标题】Python:打印自定义异常时超出最大递归深度【英文标题】:Python: Maximum recursion depth exceeded when printing custom exception 【发布时间】:2014-02-18 22:58:16 【问题描述】:

以下代码抛出RuntimeError: maximum recursion depth exceeded while getting the str of an object。我可以用两种不同的方式解决无限递归,但我不明白为什么每个修复都有效,因此不知道使用哪个,或者是否正确。

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" ) )

如果我删除 super,代码会运行但不会打印任何内容。这没有任何意义,因为根据这篇文章,Difference between __str__ and __repr__ in Python,省略__str__ 将调用__repr__,但这似乎不会发生在这里。

如果我保持对super 的调用并添加__str__ = __repr__,那么我会得到预期的输出并且没有递归。

有人可以解释为什么存在无限递归,为什么每次更改都会解决无限递归,以及为什么一种修复可能优于另一种修复?

【问题讨论】:

__repr__ 未被用作__str__ 的原因是基本异常定义了__str__ 【参考方案1】:

您的super 调用错误:不应再次提供self,它已由super 注入。这样,file_error.args[0] is file_error 因为您将self 作为额外参数传递给异常构造函数。这应该很明显为什么修复#1(完全删除超级调用)会有所帮助,但最好的修复当然是传递正确的参数

super(FileError, self).__init__(filename, *a, **k)

无限递归的原因:首先,只有object.__str__代表__repr__BaseException 分别定义了__str____repr__,所以str() 的异常调用了重载,而不是你的__repr__BaseException.__str__ 通常是 prints the args tuple(将使用 repr),但当它包含单个参数时,它会打印该单个参数的 str()

这会再次调用BaseException.__str__,依此类推。修复 #2 通过首先不输入 BaseException.__str__ 来防止此循环,而是使用根本不接触 args 元组的 __repr__

【讨论】:

@BleedingFingers 是的,很有可能。感谢您的提醒。 @delnan 我将此标记为最正确的答案,因为它回答了递归发生的原因,并且这样做很好地描述了super() 的工作原理。我现在明白为什么会有递归,也明白我必须覆盖__str__【参考方案2】:

不要将self 传递给__init__ 作为第一个参数。这导致了递归。

应该是:

super( FileError, self ).__init__( filename, *a, **k )

递归是因为

>>> print Exception("Abc")
Abc

Exception 打印第一个参数。因此,当您使用 self 初始化 FileError 的基类,即 Exception 时,它从打印第一个参数的父级继承 __str__(希望您在语句中看到递归)。因此,您将获得无限递归.

__str__ = __repr__ 覆盖继承的__str__ 并缓解无限递归。

【讨论】:

这究竟是如何导致递归的?我自己很快就发现了这个错误,但我对这如何导致str 无限递归而repr 工作正常感到目瞪口呆。 我想我只是自己想通了。不过,我仍然很想看看你的解释。 我认为这是因为 self 被用作基本异常 message 参数。所以 str 在message 上被调用,它调用str,它在消息上调用str,等等。【参考方案3】:

此行不正确:

super( FileError, self ).__init__( self, *a, **k )

您需要在super() 中传递self,但不能再次作为__init__ 的参数。所以它必须是:

super( FileError, self ).__init__( *a, **k )

【讨论】:

以上是关于Python:打印自定义异常时超出最大递归深度的主要内容,如果未能解决你的问题,请参考以下文章

RecursionError:重命名列条目后调用 Python 对象时超出最大递归深度

Python:调用 Python 对象时超出了最大递归深度

调用 Python 对象进行连续场景更新时超出最大递归深度

MongoEngine 0.5:RuntimeError:调用 Python 对象时超出最大递归深度

pyinstaller 创建 EXE RuntimeError:调用 Python 对象时超出最大递归深度

获取错误“调用Python对象时超出了最大递归深度”