标识作为调用方范围内的字符串列表的变量



我想标识用户在其交互式shell中定义的所有字符串列表。

def lists_of_strings():
d = dict(globals(), **locals())
d = {k:v for (k, v) in d.items() if not k.startswith("_") and k != 'In'}
res = {}
for k, v in d.items():
if isinstance(v, list):
if all(map(lambda x: isinstance(x, str), v)):
res[k] = v
return res

当我在当前的 shell 中定义这个函数时,它可以工作:

>>> lists_of_strings()
{}
>>> mylist = ["a", "b"]
>>> lists_of_strings()
{"mylist": ["a", "b"]}

现在,如果我在模块mymodule中移动此函数并导入它:

>>> from mymodule import lists_of_strings
>>> lists_of_strings()
{}
>>> mylist = ["a", "b"]
>>> lists_of_strings()
{}

该函数始终返回一个空字典。为什么会这样,更重要的是,我可以修复它吗?

一些上下文:我正在尝试在我的模块中编写一个帮助程序,以识别用户在当前 jupyter 笔记本中定义的合适变量。我的目标是询问用户是否要将这些变量用作某个预定义函数的参数。

Python 的内置globals返回包含当前模块全局变量的字典。这是一个相当不错的功能,因为"全局"变量不是进程范围的,而模块(因此是文件(宽 - 允许每个模块作为命名空间工作。这简化了许多工作,以避免大型系统中的名称冲突(如果使用第三方组件,则更是如此(。

但是,是的,有一种方法可以处理另一个运行上下文的全局变量。所有 Python 代码都在frame对象的上下文中运行 - 它保留对当前使用的实际字节码、局部变量和全局变量的引用。框架对象的 f_globals 属性实际上是内置globals返回的同一字典。

因此,您所要做的就是获取对发生函数调用的框架对象的引用 - 这可以通过获取当前帧的.f_back属性来完成。

这意味着,如果将函数更改为:

import sys
def lists_of_strings():
caller_frame = sys._getframe().f_back
d = dict(caller_frame.f_globals, **caller_frame.f_locals)
d = {k:v for (k, v) in d.items() if not k.startswith("_") and k != 'In'}
res = {}
for k, v in d.items():
if isinstance(v, list):
if all(map(lambda x: isinstance(x, str), v)):
res[k] = v
return res

也就是说,重要的是要注意并非所有代码都需要使用框架。如果你想简单地知道已知模块中的全局变量,你可以利用该模块的__dict__属性:

import math
print (math.__dict__)

最新更新