我正在尝试提取创建特定变量的行号。这听起来像是一个简单的问题,毕竟,我可以遍历代码行并尝试搜索或执行正则表达式。但这可能是错误的行号,如果可能的话,我希望按原样解析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)
在创建其他类(__prepare__
和Parent
)之前,将调用元类的Child
方法。它将在其名称空间中添加一些内容(在本例中为register
函数)。它直接在Child
的类主体中使用。 register
函数(实际上是元类本身的静态方法)没有做很多事情,只是在第一次调用它的地方进行打印。这对我来说已经足够了,尽管正如我已经说过的那样,这是一种解决方案,感觉好像没有解决太多问题。公开意见!
答案 0 :(得分: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()