【问题标题】:Is there a way to get a dict object with nonlocal variables?有没有办法获得带有非局部变量的 dict 对象?
【发布时间】:2014-04-21 16:55:25
【问题描述】:

我想知道,Python 中有没有一个函数可以返回一个包含在封闭函数中使用的非局部变量的 dict 对象?像 vars() 或 locals() 用于局部变量或globals() 用于全局变量。

更新: 正如 thebjorn 所指出的,嵌套函数中实际使用的非局部变量包含在local 列表中。在3.2.3下面的代码

>>> def func1():
...     x=33
...     def func2():
...             # Without the next line prints {}
...             print(x)
...             print(locals())
...     func2()
... 
>>> func1()

返回{'x': 33}。

【问题讨论】:

  • 快速测试表明 nonlocal 名称包含在 Python 3.3 中的 locals 中,您使用的是什么版本?
  • 不,它们只有在本地使用时才会包含在本地...
  • @delnan 我正在使用 3.2.3。在此版本中,locals() 如果在嵌套函数中使用,则返回 {}。
  • 请展示您用来确定这一点的程序,因为除非 ideone.com 对我撒谎,否则它确实在 3.2.3 中也可以工作。我怀疑你的测试有缺陷。
  • locals() 只返回本地变量和 free 变量。如果你的内部函数中有一个非本地函数不是空闲的(即你没有使用它),那么locals() 将不会列出它。

标签: python python-3.x


【解决方案1】:

接受的答案非常非常错误——f_back 给出了调用者,而不是词法上的父范围!

Python 是词法作用域,而不是动态作用域!

你想要的可以使用here描述的方法来完成:

def nonlocals():
    import inspect
    caller = inspect.currentframe().f_back
    return {k: v for k, v in caller.f_locals.items() if k in caller.f_code.co_freevars}

【讨论】:

  • ehm.. 你不是在上面使用.f_back(链接的答案没有这样做)。
  • @thebjorn:是的,没关系。链接的答案不需要这样做,因为它的调用者已经调用了inspect.currentframe() 并提供了它的起始堆栈帧,所以答案不需要这样做。但是,这里没有提供给我的起始frame,所以我需要自己通过检查堆栈来获取它。这与变量查找机制完全分开。我不在这里做的是使用.f_back 链动态查找变量(请参阅已接受答案中循环内的.f_back),这就是该答案的问题。
  • 啊,我明白了。你也能解决最初的问题吗,即获取所有不在globals() 和locals() 中的词法范围变量的字典?意思是,从 OPs 代码返回{'x':33},而不需要通过使用它(这里通过调用 print)或使用非本地语句声明它来将变量拉入本地?
  • @thebjorn:不幸的是,据我所知这是不可能的,因为封闭的函数不存储对其封闭函数的引用。它只存储对直接包含闭包变量的单元格的引用,这只发生在闭包中的变量。事实上,如果变量不在闭包中,那么此时它们甚至可能不存在。 (换句话说,这将要求 Python 程序无限期地存储自己的执行历史,永远不要丢弃它,因为以后可能会决定从某个死帧中查找变量。)
【解决方案2】:

没有内置的nonlocals(),但你可以自己创建一个:

def nonlocals():
    import inspect
    stack = inspect.stack()
    if len(stack) < 3: return {}
    f = stack[2][0]
    res = {}
    while f.f_back:
        res.update({k:v for k,v in f.f_locals.items() if k not in res})
        f = f.f_back
    return res

如果我在你的程序上运行它,我会得到:

{'func2': <function func1.<locals>.func2 at 0x0000000002A03510>, 'x': 33}

【讨论】:

  • 如果没有命令行选项-X:FullFrames,这将无法在 IronPython 中工作,这会影响性能,只能用于调试。一般来说,Python 的实现不必支持堆栈帧检查。
  • 不要用这个,大错特错!!它使用动态范围而不是词法范围来查找变量!
猜你喜欢
  • 2019-07-05
  • 2014-05-17
  • 1970-01-01
  • 2010-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-27
  • 2017-09-01
相关资源
最近更新 更多