获取定义变量的行号
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 属性的行号。使用 ast
? dis
?我不反对任何建议。
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(Parent
和 Child
)之前被调用。它在它们的命名空间中添加了一些东西(在这种情况下,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()
我正在尝试提取创建特定变量的行号。这听起来像是一个简单的问题,毕竟,我可以遍历代码行并尝试搜索,或者做一个正则表达式。但这可能是一个错误的行号,如果可能的话,我更愿意按原样解析 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 属性的行号。使用 ast
? dis
?我不反对任何建议。
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(Parent
和 Child
)之前被调用。它在它们的命名空间中添加了一些东西(在这种情况下,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()