找出引起异常的对象的变量名

时间:2018-10-04 21:42:55

标签: python python-3.x exception

让我们考虑以下代码示例,我将使用它来引发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除块之外找到它?

3 个答案:

答案 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']

最终结果是一个列表,其中包含所有局部变量名称(在调用的函数中使用),这些名称对应于我们的包装输入,从而导致了异常。