获取定义变量的行号

Get line number where variable is defined

我正在尝试提取创建特定变量的行号。这听起来像是一个简单的问题,毕竟,我可以遍历代码行并尝试搜索,或者做一个正则表达式。但这可能是一个错误的行号,如果可能的话,我更愿意按原样解析 Python 代码并获得一个真实的行号。

这是我的:

class MyClass:

    """
    MyClass.

    Notice that we do override the name class attribute.

    class MyClass:

        name = "a real name"

    """

    name = "the true name"

    def __iinit__(self):
        pass

感谢 inspect.getsourcelines,我可以得到 class 的行(一个文件中可能定义了多个 class,它们可能都有不同的 name 属性)。我需要的是找到为此 class 定义 name class 属性的行号。使用 astdis?我不反对任何建议。

This is useful for debugging. The class attribute in my case is a multi-line string containing some formatted text and I need to report the error with file name and line number when problems occur. So I would rather know where the attribute is defined, not just have its value.

感谢您的帮助,

编辑

根据@bsbueno 的回答,我试图想出一些简单的东西,但不会影响我的原始设计,并尽可能流畅。我正在创建一个简单的 metaclass,它的唯一工作就是注册这个属性。请注意,我仍然是 "wrapping" Child class 中的函数调用中的属性,因此此解决方案并不理想,并且并不比定义 register 好多少在单独的命名空间中运行。总而言之,我所做的不是最好的选择,但它提供了另一个例子,可能对其他人有用,或者我希望:

import sys

class MetaRegister(type):

    def __prepare__(name, bases, **kwds):
        return {"register": MetaRegister.register}

    @staticmethod
    def register(value):
        frame = sys._getframe().f_back
        file = frame.f_globals["__file__"]
        line = frame.f_lineno
        print(f"Calling register {file}:{line}: {value!r}")
        return value

class Parent(metaclass=MetaRegister):

    option = None

class Child(Parent):

    option = register(3)

metaclass' __prepare__ 方法在创建其他 classes(ParentChild)之前被调用。它在它们的命名空间中添加了一些东西(在这种情况下,register 函数)。这直接用在 Child 的 class 正文中。 register 函数(实际上是 metaclass 本身的一个静态方法)除了在它第一次被调用的地方打印外并没有做太多事情。这对我来说已经足够了,尽管正如我所说,这是一个解决方案,感觉它并没有解决太多问题。发表意见!

虽然对于函数和全局变量,这或多或少会很简单。很难怪,使用 "dis",对于 class 属性,这可能会更加复杂 - 因为 class 主体首先被编译为代码对象,即 运行 一旦class 已定义,一旦 class 被正确创建,除 "thrown away" 之外的所有内容。

此时您只有 class'__dict__,根本没有任何创建顺序或创建地点的提示。

此外,使用 dis 可以通过搜索 STORE_NAME 操作码找到变量的属性,你有同样的问题不知道 哪个 其中 运行 - 但它并不比对源代码本身的模式进行文本搜索更可靠。

执行此操作的唯一可靠方法是,如果不使用常规变量,而是使用特殊描述符 - 这些描述符可以检查调用以设置其值的位置,并在注册表中对其进行注释。

因此,您只需要将要跟踪的变量放在一个特殊对象中,该对象可以有一个短名称 - 最终代码如下所示:

from variable_registry import R

class MyClass:

   R.name = "the true name"

   def __init__(self):
        print(R.name)

如果您需要实例变量,这将需要特殊处理 - 但它仍然可以通过更复杂的机制来完成。对于 class 和全局变量,您最好在 non-concurrent 执行中使用局部变量。

import sys

class Registry:
    def __init__(self):
        self.__dict__["_registry"] = {}

    def __setattr__(self, name, value):
        frame = sys._getframe().f_back
        file = frame.f_globals["__file__"]
        self.__dict__["_registry"][name] = file, frame.f_lineno, value
        super().__setattr__(name, value)


R = Registry()