AttributeError and lost exception message
似乎Python处理AttributeError异常非标准。
当类定义__getattr__方法时,它会吞下此异常,而不是进一步传播到堆栈顶部。 原来的例外是否丢失了?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class A(object):
@property
def test(self):
raise AttributeError('message which should not be lost')
return 'this would never return'
def __getattr__(self, name):
print 'Trying get attribute: ', name
# how decide if AttributeError was already raised ??
return 42
a = A()
print a.test
# Trying get attribute: test
# 42 |
想象一下,AttributeError异常可能出现在调用链中任意深度的任何地方。
问题是如何使用'message which should not be lost'消息保留原始异常实例? 是否有一些方法可以保持AttributeError而无需求助于替换为不同的异常类的变通方法?
-
不要混合属性,然后__getattr__。 只要__getattribute__遇到AttributeError,就会调用__getattr__。 是的,异常可能会丢失。
-
@MartijnPieters不幸的是有时候无法避免。 例如,我正在使用基于Django的商店应用程序,它在Form子类中引入了__getattr__。 在表单的验证中,所有属性异常都被静音,它会让我发现错误来自哪里。
您通过引发AttributeError为object.__getattribute__()处理程序提供该属性不存在的信号。 然后定义的行为是调用__getattr__。 异常丢失,由__getattribute__处理。 从文档:
Called unconditionally to implement attribute accesses for instances of the class. If the class also defines __getattr__(), the latter will not be called unless __getattribute__() either calls it explicitly or raises an AttributeError.
如果您不希望__getattribute__处理异常,则需要将__getattr__行为转移到自定义__getattribute__方法:
1 2 3 4 5 6 7 8 9 10 11 12 13
| class A(object):
@property
def test(self):
raise AttributeError('message which should not be lost')
return 'this would never return'
def __getattribute__(self, name):
try:
value = super(A, self).__getattribute__(name)
except AttributeError as ae:
# chance to handle the attribute differently
# if not, re-raise the exception
raise ae |
请注意,hasattr()函数的行为方式相同; 当尝试访问该属性时引发异常时,它将返回False。
-
谢谢。为了清楚起见,如果类定义__getattr__,则无论如何定义__getattribute__,都没有其他方法可以在其中重新引用AttributeError来传播异常。如果需要保存异常实例,需要在__getattribute__中进行吗?
-
@JoanBlackmoore:你必须完全重新实现__getattribute__(所以不要调用super()版本)以避免它吞噬异常,或者你需要重命名__getattr__方法然后从中调用重命名的版本__getattribute__覆盖我已经显示了。
-
再次感谢,虽然听起来有点hackish。顺便说一句。在getattr方法中的任何地方调用hasattr似乎会导致无限递归。您可能意味着在属性访问位置使用它,但在使用第三方代码时几乎不可能这样做。
-
@JoanBlackmoore:是的,hasattr()触发__getattribute__,如果属性不存在,可以触发__getattr__。属性访问和__getattr__可能会变得非常复杂并导致难以诊断的问题,例如,请参阅nedbatchelder.com/blog/201010/surprising_getattr_recursion.html。
-
如果在__getattribute__中将异常实例分配给类实例变量(或者如果没有引发则清除),然后在__getattr__中测试它,你会发现一些缺点吗?
-
@JoanBlackmoore:问题是你需要调用的object.__getattribute__实现来实现描述符处理(一个property是一个描述符,就像函数一样)调用__getattr__;当__getattr__存在时,您无法拦截AttributeError的吞咽。