如何获取缺少属性的对象

fly*_*eep 7 python exception inspection

假设我们尝试访问不存在的属性:

>>> {'foo': 'bar'}.gte('foo')  # well, I meant “get”!
Run Code Online (Sandbox Code Playgroud)

Python AttributeError只有args一个包含已完成错误消息的字符串的属性:'dict' object has no attribute 'gte'

使用inspect和/或traceback模块sys.last_traceback,是否有办法获取实际的dict对象?

>>> offending_object = get_attributeerror_obj(sys.last_traceback)
>>> dir(offending_object)
[...
 'clear',
 'copy',
 'fromkeys',
 'get',       # ah, here it is!
 'items',
 ...]
Run Code Online (Sandbox Code Playgroud)

编辑:因为猫无论如何都不在了,我会分享我的发现和代码(请不要解决这个问题并提交给PyPI,请;))

AttributeError是在这里创建的,它表明显然没有引用附加的原始对象.

这里的代码具有相同的占位符函数:

import sys
import re
import difflib

AE_MSG_RE = re.compile(r"'(\w+)' object has no attribute '(\w+)'")

def get_attributeerror_obj(tb):
    ???

old_hook = sys.excepthook

def did_you_mean_hook(type, exc, tb):
    old_hook(type, exc, tb)

    if type is AttributeError:
        match = AE_MSG_RE.match(exc.args[0])
        sook = match.group(2)

        raising_obj = get_attributeerror_obj(tb)

        matches = difflib.get_close_matches(sook, dir(raising_obj))
        if matches:
            print('\n\nDid you mean?', matches[0], file=sys.stderr)

sys.excepthook = did_you_mean_hook
Run Code Online (Sandbox Code Playgroud)

Vee*_*rac 1

这不是你想要的答案,但我很确定你不能......至少不能sys.excepthooksys.excepthook这是因为随着帧的展开,引用计数会递减,因此在调用之前对对象进行垃圾收集是完全有效的。事实上,这就是 CPython 中发生的情况:

import sys

class X:
    def __del__(self):
        print("deleting")

def error():
    X().wrong

old_hook = sys.excepthook
def did_you_mean_hook(type, exc, tb):
    print("Error!")
sys.excepthook = did_you_mean_hook

error()
#>>> deleting
#>>> Error!
Run Code Online (Sandbox Code Playgroud)

也就是说,情况并非总是如此。因为异常对象指向框架,如果您的代码如下所示:

def error():
    x = X()
    x.wrong
Run Code Online (Sandbox Code Playgroud)

x尚无法收集。x属于框架所有,并且框架是活动的。但由于我已经证明没有明确引用该对象,因此该做什么并不明显。例如,

def error():
    foo().wrong
Run Code Online (Sandbox Code Playgroud)

可能有也可能没有存活下来的对象,唯一可行的方法就是运行foo……但即使如此,你也会遇到副作用问题。

所以不,这是不可能的。如果您不介意采取任何措施,您可能最终不得不在加载时重写 AST(类似于FuckIt.py)。但你不想这样做。


我的建议是尝试使用 linter 来获取所有已知类及其方法的名称。您可以使用它来对回溯字符串进行反向工程以获取类和不正确的方法,然后运行模糊匹配来查找建议。