让我们考虑以下代码示例,我将使用它来引发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())
我将收到AttributeError,因为第二个虚拟对象没有属性age
。这是由second.age
引起的。
我现在的问题是,是否有一种方法可以找出导致错误的变量名。查看源代码,很明显它是second
,但是我如何在try除块之外找到它?
答案 0 :(得分:2)
出于调试目的,请注意该错误消息解释了发生的情况。
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)
答案 1 :(得分:0)
您可以更改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
时,就可以将其跟踪到特定的行。
答案 2 :(得分:0)
好吧,所以我找到了一个解决方案,该解决方案在类不受我控制的情况下也应该起作用。此解决方案仅针对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对象包装我们传递给测试函数的每个值。
此代理对象通过设置AttributeError
标志来记录是否看到_had_exception
。
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']
最终结果是一个列表,其中包含所有局部变量名称(在调用的函数中使用),这些名称对应于我们的包装输入,从而导致了异常。