【问题标题】:Find out variable name of object that caused an exception找出导致异常的对象的变量名
【发布时间】:2018-10-04 21:42:54
【问题描述】:

让我们考虑以下代码示例,我将使用它来引发 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,因为第二个 Dummy 没有属性 age。这是由second.age 引起的。

我现在的问题是,是否有一种方法可以找出导致错误的变量名称。看源码很明显是second,但是怎么在try except块中找到呢?

【问题讨论】:

  • 为什么你的except 块必须知道是哪个对象导致了错误?
  • 不太清楚你的意思。您想查看运行时的回溯吗? stackoverflow.com/q/3702675/4180176
  • @Aran-Fey,我稍后要做的是某种形式的类型重建,我尝试通过测试一个函数来收集输入的约束(例如,所需的类型必须具有年龄属性)多个(可能是错误的)输入。如果我知道导致它的变量的名称,我可以找出我可以添加约束的地方。
  • @JoshuaNixon,查看回溯似乎是一个开始,但我能从中得到的只是AttributeError("'Dummy' object has no attribute 'name'",)。但这里 Dummy 是变量的类型,而不是它的名称。
  • 在 AttributeError 的特定情况下,您能否不解析异常并检查属性和类名与您的参数?此外,一些例外情况包括比消息更多的信息。

标签: python python-3.x exception


【解决方案1】:

出于调试目的,请注意错误消息解释了发生的情况。

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)

【讨论】:

  • 感谢您的广泛回答。我认为如果测试功能在我的控制之下,这将起作用。但是,如果它是我无法更改的类的一部分,我将不会获得变量名。
  • 变量名是什么意思?在你测试?在原始代码中?
  • 在测试方法中我调用second.age 导致错误。我想找出导致错误的是名称/标识符second 的变量。也许这是不可能的,我不知道
  • 然后使用第二种解决方案:专门在这一行周围放一个try except块
【解决方案2】:

您可以更改您的 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 时,您将能够将其追踪到特定的行。

【讨论】:

  • 是的,这行得通。但是,即使导致错误的功能不在我的控制之下,我也会尝试找到一个有效的解决方案。
【解决方案3】:

好的,所以我找到了一个解决方案,如果课程不在我的控制之下,它也应该可以工作。此解决方案仅针对 AttributeError,但应可扩展,以防需要捕获其他错误。

我们仍然有相同的测试函数和相同的 Dummy 类

def test(first, second):
    print("My name is " + first.age + " and I am here with " + second.age)

class Dummy(object):
    def __init__(self):
        pass

我们可以使用代理对象来包装我们传递给测试函数的每个值。 此代理对象通过设置_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']

最终结果是一个包含所有局部变量名称的列表——在被调用函数中使用——它们对应于我们包装的输入,导致异常。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-25
    • 1970-01-01
    • 2018-08-07
    • 2018-11-11
    • 1970-01-01
    • 1970-01-01
    • 2010-10-24
    • 1970-01-01
    相关资源
    最近更新 更多