在Python中获取运行时局部变量的名称

时间:2013-07-11 01:15:16

标签: python debugging

我应该通过以下说法来说明这一点:我知道默认情况下不支持此功能 - 我正在尝试的是一种实用性很少的hacky解决方法,并且是精神手淫的完整练习,因为无聊和好奇心。那就是说,我正在努力做到以下几点:

基于以下Python代码,

with BuildFile('mybuild.build') as buildfile:
    objdir = 'obj'

我想生成一个文件mybuild.build,内容为:

objdir = obj

理想情况下,我想在创建点关联变量名称,这样如果我在objdir = 'obj'之后设置断点,我希望能够执行以下操作:< / p>

>>> print repr(objdir)
'objdir = obj'

然而,使用内置功能是不可能的,因为无法覆盖从语法中推断出的类型。我可能最终在使用BuildFile.__enter__方法的ctypes方法中修复了一种变通方法,以便对基础tp_new结构上的tp_dictPyTypeObject字段进行修补(以及随后在退出时恢复该覆盖),但为了简单起见,我们假设我没有关联变量名称,直到我到达BuildFile.__exit__方法。

我想知道的是以下内容:

是否内置Python功能可以暂停执行,追溯到声明了局部变量的帧,并获取与变量关联的本地名称?

3 个答案:

答案 0 :(得分:2)

实际上你可以像这样执行类似的技巧:

>>> from contextlib import contextmanager
>>> @contextmanager
def override_new_vars(locs):
    old_locals = locs.copy()
    yield
    new_locals_names = set(locs) - set(old_locals)
    for name in new_locals_names:
        locs[name] = '%s = %r' % (name, locs[name])


>>> with override_new_vars(locals()):
    c = 10


>>> c
'c = 10'

在你的情况下,它看起来像:

with BuildFile('mybuild.build') as buildfile, override_new_vars(locals()):
    objdir = 'obj'

那是你想做的吗?

答案 1 :(得分:1)

Python没有可追踪框架的可移植方式......但CPython实现确实:sys._getframe返回一个框架对象。

你可以用框架对象做什么?请参阅inspect文档中有用的所有有趣内容的方便图表,但它们包括框架所见的locals()globals(),以及框架中执行的代码对象 - 它本身包括本地名称,未绑定名称和闭包单元格。

但是,正如其他人所指出的那样,你并不需要这个框架;你需要的只是本地人,而且只是明确地将它传递给你的上下文管理器就更简单了。


如果你真的想这样做:

import contextlib
import sys

@contextlib.contextmanager
def dumping():
    f = sys._getframe(2)
    fl = f.f_locals.copy()
    try:
        yield None
    finally:
        for name, value in f.f_locals.items():
            if name not in fl:
                print('{} = {}'.format(name, value))

bar = 0
def foo():
    global bar
    bar = 3
    baz = 4
    qux = 5
    with dumping():
        spam = 'eggs'
        eggs = 3
        bar = 4
        baz = 5

foo()

运行时,应该打印:

eggs = 3
spam = eggs

换句话说,只有在with块内声明的新变量的名称和值 - 我认为,你想要的是什么,对吗?


如果你想要新的和反弹的本地人,你可能想要存储这样的东西:

fl = {(name, id(value)) for name, value in f.f_locals.items()}

当然你也可以重新绑定nonlocals和globals,所以如果你关心它,也要隐藏全局变量(但确保检查locals is globals是否有模块级代码),或者走一下闭包。 / p>


如果您正在使用CPython 2(为什么?对于真正的项目,它有时是有意义的,但要了解内部如何工作以获得乐趣?然而,有些人......),相同的代码将起作用。属性名称可能略有不同,但您可以通过转出框架和代码的dir来猜测它们。显然你想要2.x print语法。

它也适用于PyPy,至少2.0b。


如果你想知道我怎么知道使用_getframe(2) ......我没有。我很确定它会是1帧或2帧,可能只有3帧,但是哪一帧?所以我就这样做了:

@contextlib.contextmanager
def dumping():
    for i in range(4):
        f = sys._getframe(i)
        print(f.f_code.co_filename, f.f_code.co_firstlineno, f.f_lineno)

0当然是dumping本身; 1是contextlib.contextmanager中的包装函数; 2是呼叫帧; 3是模块顶层。一旦你想到这一点就很明显,但直到我知道答案才明白。 :)

答案 2 :(得分:0)

我想你可以试试这个:

from copy import copy
objdir = 'obj'

def get_variable_name(variable):

    if variable:
        variables = copy(locals())
        for i in variables:
            if variables.get(i) == variable:
                res = i + '=' + variables.get(i)
                return res

使用此应该可以解决问题,我自己测试了这段代码。

相关问题