让我们考虑下面的代码示例,我将使用它来引发 AttributeError 作为示例。
def test(first, second):
print("My age is " + first.age + " and my neighbour is " + second.age)
假设我有以下课程。
class Dummy(object):
def __init__(self):
pass
如果我调用函数
d = Dummy()
d.__setattr__("age", "25")
test(d, Dummy())
我会得到一个属性错误,因为第二个假人没有属性age
。这是由second.age
引起的。
我现在的问题是,是否有一种方法可以找出导致错误的变量的名称。查看源代码很明显它是second
,但是我如何在尝试中找出它除了块?
出于调试目的,请注意错误消息解释了发生的情况。
obj = object()
print(obj.does_not_exist)
错误信息
AttributeError: 'object' object has no attribute 'does_not_exist'
因此,很明显哪个属性引发了异常。如果您认为在运行时可能需要该信息,也可以通过sys.exc_info
函数恢复该信息。
缩小try-except
范围如果这不能让您满意,请注意,try-except
语句的目的是捕获您期望发生的异常。因此,如果同一块中可能出现两个不同的异常,则不妨将其拆分为两个try-except
语句。
def test(first, second):
try:
first_age = first.age
except AttributeError:
# Do something if first doest not have attribute age
try:
second_age = second.age
except AttributeError:
# Do something if second does not have attribute age
print("My age is " + first.age + " and my neighbour is " + second.age)
使用hasattr
另一种选择可能是使用hasattr
来检查该属性是否存在。
def test(first, second):
if not hasattr(first, 'age'):
# Do something
if not hasattr(second, 'age'):
# Do something else
print("My age is " + first.age + " and my neighbour is " + second.age)
您可以更改test
定义以拆分属性的访问:
def test(first, second):
f_age = first.age
s_age = second.age
print(f"My age is {f_age} and my neighbour is {s_age}")
然后,当您调用test
时,您将能够将其跟踪到特定行。
好的,所以我找到了一个解决方案,如果类不受我的控制,它也应该有效。此解决方案仅针对AttributeError
但应可扩展,以防需要捕获其他错误。
我们仍然具有相同的测试函数和相同的虚拟类
def test(first, second):
print("My name is " + first.age + " and I am here with " + second.age)
class Dummy(object):
def __init__(self):
pass
我们可以使用 Proxy 对象来包装我们传递给测试函数的每个值。 此代理对象通过设置_had_exception
标志来记录它是否看到AttributeError
。
class Proxy(object):
def __init__(self, object_a):
self._object_a = object_a
self._had_exception: bool = False
def __getattribute__(self, name):
if name == "_had_exception":
return object.__getattribute__(self, name)
obj = object.__getattribute__(self, '_object_a')
try:
return getattr(obj, name)
except AttributeError as e:
# Flag this object as a cause for an exception
self._had_exception = True
raise e
对函数的调用如下所示
d = Dummy()
d.__setattr__("age", "25")
p1 = Proxy(d)
p2 = Proxy(Dummy())
try:
test(p1, p2)
except AttributeError as e:
# Get the local variables from when the Error happened
locals = e.__traceback__.tb_next.tb_frame.f_locals
offender_names = []
# Check if one of the local items is the same
# as one of our inputs that caused an Error
for key, val in locals.items():
if p1._had_exception:
if p1 is val:
offender_names.append(key)
if p2._had_exception:
if p2 is val:
offender_names.append(key)
print(offender_names) # ['second']
最终结果是一个列表,其中包含所有局部变量名称 - 在调用的函数中使用 - 对应于我们包装的输入,这导致了异常。