2

我应该先说以下内容:我知道默认情况下不支持此功能-我正在尝试的是一种hacky解决方法,几乎​​没有实际应用,并且由于无聊和好奇,它是一种完整的心理自慰练习. 也就是说,我正在尝试执行以下操作:

基于以下 Python 代码,

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

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

objdir = obj

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

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

但是,使用内置功能是不可能的,因为无法覆盖从语法中推断出的类型。我最终可能会在用于对底层结构上的or字段进行猴子修补的BuildFile.__enter__方法中破解一个解决方法(并随后在退出时恢复该覆盖),但为了简单起见,我们假设我没有关联变量名称直到我找到方法。ctypestp_newtp_dictPyTypeObjectBuildFile.__exit__

我想知道的是以下几点:

是否有内置的 Python 功能用于停止执行、回溯到声明局部变量的帧以及获取与变量关联的局部名称?

4

3 回答 3

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'

那是你想做的吗?

于 2013-07-11T01:30:26.160 回答
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()}

当然,你也可以重新绑定非局部变量和全局变量,所以如果你关心这个,要么也存储全局变量(但一定要检查locals is globals模块级代码),或者遍历闭包。


如果您使用的是 CPython 2(为什么?对于实际项目,它有时是有意义的,但是为了好玩而学习内部如何工作?然而,有些人……),相同的代码也可以工作。属性名称可能略有不同,但您可以通过转储dir框架和代码来猜测它们。显然你想要 2.xprint语法。

它也适用于 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是模块顶层。仔细想想,这很明显,但在我知道答案之前并不明显。:)

于 2013-07-11T01:58:38.253 回答
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

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

于 2013-07-11T01:53:17.970 回答