【问题标题】:Get line number where variable is defined获取定义变量的行号
【发布时间】:2019-06-08 13:41:44
【问题描述】:

我正在尝试提取创建特定变量的行号。这听起来像一个简单的问题,毕竟我可以遍历代码行并尝试搜索,或者做一个正则表达式。但这可能是一个错误的行号,如果可能的话,我更愿意按原样解析 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,我可以获得类的行(一个文件中可能定义了多个类,它们可能都有不同的name 属性)。我需要的是找到为此类定义 name 类属性的行号。使用ast? dis?我不反对任何建议。

这对于调试很有用。在我的例子中,class 属性是一个包含一些格式化文本的多行字符串,当出现问题时我需要用文件名和行号报告错误。所以我宁愿知道属性是在哪里定义的,而不仅仅是它的值。

感谢您的帮助,

编辑

根据@bsbueno 的回答,我试图想出一些简单的东西,不会干扰我的原始设计,可能是这样流动的。我正在创建一个简单的元类,它唯一的工作就是注册这个属性。请注意,我仍然在 Child 类的函数调用中“包装”该属性,因此这个解决方案并不理想,并且比在单独的命名空间中定义 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)

在创建其他类(ParentChild)之前调用元类的 __prepare__ 方法。它在它们的命名空间中添加了一些东西(在本例中为register 函数)。这直接在Child 的类主体中使用。 register 函数(它实际上是元类本身的一个静态方法)并没有做太多的事情,只是在第一次调用它的地方打印。这对我来说已经足够了,尽管正如我所说,这是一个可能感觉解决不了多少的解决方案。发表意见!

【问题讨论】:

  • __iinit__ ?双i?
  • 我猜这是个错误,抱歉。

标签: python debugging inspect


【解决方案1】:

虽然对于函数和全局变量,这或多或少是直截了当的。难怪,使用“dis”,对于类属性,这可能更加复杂 - 因为类主体首先编译为代码对象,该对象在定义类时运行一次,并且一旦类被“丢弃”已正确创建。

此时您只有类'__dict__,根本没有创建顺序或创建位置的提示。

此外,使用dis 可以通过搜索STORE_NAME 操作码来查找变量的归属位置,您也有同样的问题,即不知道其中运行了哪些 - 但它不会比对源代码本身的模式进行文本搜索更可靠。

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

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

from variable_registry import R

class MyClass:

   R.name = "the true name"

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

如果您需要实例变量,这将需要特殊处理 - 但仍然可以通过更复杂的机制来完成。对于类和全局变量,您可以像在非并发执行中使用局部变量一样。

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()

【讨论】:

  • 谢谢,这听起来确实是一个我没有考虑过的好解决方案。我不反对将 str 包装在属性定义中。我什至不知道这种语法是可能的。好吧,生活和学习!
猜你喜欢
  • 2012-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-02
  • 1970-01-01
  • 1970-01-01
  • 2018-08-05
  • 1970-01-01
相关资源
最近更新 更多