获取当前作用域中所有变量及其值的字典

51

考虑以下代码片段:

globalVar = 25

def myfunc(paramVar):
    localVar = 30
    print "Vars: {globalVar}, {paramVar}, {localVar}!".format(**VARS_IN_SCOPE)

myfunc(123)

我想要获取包含globalVarparamVarlocalVar在内的所有变量信息,存在一个名为VARS_IN_SCOPE的字典中。

我希望能够引用字符串所在的所有变量。因此预期的输出是:

Vars: 25, 123, 30

通过将**dict(globals().items() + locals().items())传递给format(),我可以实现这一点。这种方法总是正确的吗?或者是否有一些特殊情况会导致此表达式处理不正确?

已重写以澄清问题。


为什么你想这样做?很有可能,有更好的方法来完成你正在尝试做的事情。 - Sasha Chedygov
我认为这个代码片段会让它变得清晰明了。我想在字符串内插入变量。当然,可能有更好的方法。请随意提出建议作为答案。 - Roman Starkov
仅仅传递 **locals() 已经有点懒惰了,但至少局部变量很容易找到。再加上全局变量,只会让每个人在代码中四处寻找你的值。广泛依赖全局变量,特别是在它们看起来根本不像变量引用的奇怪上下文中使用它们,通常对开发不利。复制本地内容,或者更好的方法是使事情明确化。 - Jon Jay Obermark
@JonJayObermark总体上同意全局变量的使用,但我在这里问的与print“Foo is:%s”%foo没有什么不同。您仍然需要“寻找”当前范围内foo的含义,它仍然可以是本地变量或全局变量。 - Roman Starkov
逻辑上是正确的,但特意散布麻烦往往不是一个好主意。从全局上获取某些东西到本地环境中可以清楚地表明它来自哪里,并为您提供机会提供文档,即使只是以更有意义的本地名称的形式。 - Jon Jay Obermark
显示剩余4条评论
6个回答

45

将两个字典合并的最佳方法是使用 dict(globals(), **locals()),其中局部变量覆盖全局变量。

合并全局变量和局部变量的方法缺失了以下内容:(a) 内置函数(我想这是故意的,即你不认为内置函数是"变量"...但是,如果你愿意,它们可以是!),以及(b) 如果您在一个嵌套的函数中,则任何局部于封闭函数的变量都会丢失(获取所有这些变量的字典没有真正好的方法,而只有在嵌套函数中明确访问的变量,即其“自由变量”,作为闭包中的单元格存续)。

我想对于您打算使用的情况,这些问题可能并不重要,但您提到了“边角情况”;-)。如果您需要解决它们,有办法获取内置函数(这很容易)和所有单元格(来自封闭函数的变量,您在嵌套函数中明确提到的——用thefunction.func_code.co_freevars获取名称,用thefunction.func_closure获取单元格,每个单元格上使用cell_contents 获取其值)。 (但请记住,在您的嵌套函数代码中,这些仅是明确访问的封闭函数中的变量!)


奇怪的是你无法从嵌套函数内部获取所有变量。有人知道为什么locals()不起作用吗?看起来应该可以! - sudo

8

这个是否达到了你的意图?

d = dict(globals())
d.update(locals())

如果我正确阅读了文档,您会创建 globals() 字典的副本,然后覆盖任何重复项并插入来自 locals() 字典的新条目(因为在您的范围内,locals() 应该具有优先权)。
我一直没有运气得到一个合适的函数来返回调用函数作用域内所有变量的完整字典。以下是代码(我只使用pprint使输出在SO上呈现出很好的格式)。
from pprint import *

def allvars_bad():
    fake_temp_var = 1
    d = dict(globals())
    d.update(locals())
    return d

def foo_bad():
    x = 5
    return allvars_bad()

def foo_good():
    x = 5
    fake_temp_var = "good"
    d = dict(globals())
    d.update(locals())
    return d

pprint (foo_bad(), width=50)
pprint (foo_good(), width=50)

输出结果:

 {'PrettyPrinter': <class pprint.PrettyPrinter at 0xb7d316ec>,
 '__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'temp.py',
 '__name__': '__main__',
 '__package__': None,
 'allvars_bad': <function allvars_bad at 0xb7d32b1c>,
 'd': <Recursion on dict with id=3084093748>,
 'fake_temp_var': 1,
 'foo_bad': <function foo_bad at 0xb7d329cc>,
 'foo_good': <function foo_good at 0xb7d32f0c>,
 'isreadable': <function isreadable at 0xb7d32c34>,
 'isrecursive': <function isrecursive at 0xb7d32c6c>,
 'pformat': <function pformat at 0xb7d32bc4>,
 'pprint': <function pprint at 0xb7d32b8c>,
 'saferepr': <function saferepr at 0xb7d32bfc>}
{'PrettyPrinter': <class pprint.PrettyPrinter at 0xb7d316ec>,
 '__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'temp.py',
 '__name__': '__main__',
 '__package__': None,
 'allvars_bad': <function allvars_bad at 0xb7d32b1c>,
 'd': <Recursion on dict with id=3084093884>,
 'fake_temp_var': 'good',
 'foo_bad': <function foo_bad at 0xb7d329cc>,
 'foo_good': <function foo_good at 0xb7d32f0c>,
 'isreadable': <function isreadable at 0xb7d32c34>,
 'isrecursive': <function isrecursive at 0xb7d32c6c>,
 'pformat': <function pformat at 0xb7d32bc4>,
 'pprint': <function pprint at 0xb7d32b8c>,
 'saferepr': <function saferepr at 0xb7d32bfc>,
 'x': 5}

请注意,第二个输出中,我们已经覆盖了fake_temp_var,并且x是存在的;第一个输出只包括在allvars_bad范围内的局部变量。
因此,如果您想访问完整的变量作用域,就不能将locals()放在另一个函数内部。

I had suspected there was some sort of frame object, I just didn't (know where to) look for it.

This works to your spec, I believe:

def allvars_good(offset=0):
    frame = sys._getframe(1+offset)
    d = frame.f_globals
    d.update(frame.f_locals)
    return d


def foo_good2():
    a = 1
    b = 2
    return allvars_good()

-->

{'PrettyPrinter': <class pprint.PrettyPrinter at 0xb7d6474c>,
 '__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'temp.py',
 '__name__': '__main__',
 '__package__': None,
 'a': 1,
 'allvars_bad': <function allvars_bad at 0xb7d65b54>,
 'allvars_good': <function allvars_good at 0xb7d65a04>,
 'b': 2,
 'foo_bad': <function foo_bad at 0xb7d65f44>,
 'foo_good': <function foo_good at 0xb7d65f7c>,
 'foo_good2': <function foo_good2 at 0xb7d65fb4>,
 'isreadable': <function isreadable at 0xb7d65c6c>,
 'isrecursive': <function isrecursive at 0xb7d65ca4>,
 'pformat': <function pformat at 0xb7d65bfc>,
 'pprint': <function pprint at 0xb7d65bc4>,
 'saferepr': <function saferepr at 0xb7d65c34>,
 'sys': <module 'sys' (built-in)>}

回答你的问题,我不知道 :) 实际上这正是我想找出来的 - 是否将两者结合起来是正确的做法。 - Roman Starkov
是的,不幸的是locals()必须在那里。globals()也是如此 - 它必须在同一个模块中才能工作。我想知道是否可以使用一些sys._getframe()的技巧来代替。 - Roman Starkov

2
您可以自己制作:

您可以自己制作:

allvars = dict()
allvars.update(globals())
allvars.update(locals())

或者将前两行合并:
allvars = dict(globals())
allvars.update(locals())

2
globalVar = 25

def myfunc(paramVar):
    localVar = 30
    all_vars = locals.copy()
    all_vars.update(globals())
    print "Vars: {globalVar}, {paramVar}, {localVar}!".format(all_vars)

myfunc(123)

1

字符串内插在最简单的情况下工作。只需列出您的变量即可。Python会为您检查本地和全局变量。

globalVar = 25

def myfunc(paramVar):
    localVar = 30
    print "Vars: %d, %d, %d!" % ( globalVar, paramVar, localVar )

myfunc(123)

这不是插值。Python没有像这样的插值,但是使用.format()函数可以接近实现。 - Roman Starkov
如果你想知道我为什么不喜欢百分号(%)- 想象一下这样的情况:print "{path}/myscript.py --input {path}/file1 --output {path}/file2".format(**locals()) - 如果使用“%”,我需要传递路径三次。当有很多变量时,这将成为一个完全难以维护的噩梦。 - Roman Starkov
@romkyns:不是这样的。字符串运算符可以接受一个字典参数,无需像format一样使用**进行解包: print“%(path)s / myscript.py --input%(path)s / file1 --output%(path)s / file2”%locals() - Don O'Donnell

0

Python 3.5或更高版本:

globalVar = 25

def myfunc(paramVar):
    localVar = 30
    print("Vars: {globalVar}, {paramVar}, {localVar}!".format(**{**locals(), **globals()}))

myfunc(123)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接