获取变量名作为字符串

新手上路,请多包涵

我已经阅读 了如何将函数名作为字符串获取? .

我怎样才能对变量做同样的事情?与函数相反,Python 变量没有 __name__ 属性。

换句话说,如果我有一个变量,例如:

 foo = dict()
foo['bar'] = 2

我正在寻找一个函数/属性,例如 retrieve_name() 以便 从此列表中在 Pandas 中创建一个 DataFrame ,其中 列名 由实际字典的名称给出:

 # List of dictionaries for my DataFrame
list_of_dicts = [n_jobs, users, queues, priorities]
columns = [retrieve_name(d) for d in list_of_dicts]

原文由 Amelio Vazquez-Reina 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 968
2 个回答

TL;博士

使用来自 python-varnameWrapper 助手:

 from varname.helpers import Wrapper

foo = Wrapper(dict())

# foo.name == 'foo'
# foo.value == {}
foo.value['bar'] = 2

对于列表理解部分,您可以执行以下操作:

 n_jobs = Wrapper(<original_value>)
users = Wrapper(<original_value>)
queues = Wrapper(<original_value>)
priorities = Wrapper(<original_value>)

list_of_dicts = [n_jobs, users, queues, priorities]
columns = [d.name for d in list_of_dicts]
# ['n_jobs', 'users', 'queues', 'priorities']
# REMEMBER that you have to access the <original_value> by d.value

我是 python-varname 包的作者。如果您有任何问题,请告诉我,或者您可以在 Github 上提交问题。

长答案

甚至可能吗?

是和否。

我们在运行时检索变量名称,因此我们需要调用一个函数以使我们能够访问先前的帧以检索变量名称。这就是为什么我们需要一个 Wrapper 那里。在该函数中,在运行时,我们正在解析前一帧中的源代码/AST 节点以获取确切的变量名称。

但是,之前帧中的源代码/AST 节点并不总是可用,或者它们可能被其他环境修改(例如: pytestassert 语句)。一个简单的例子是代码通过 exec() 运行。即使我们仍然能够从字节码中检索一些信息,但它需要太多的努力,而且也容易出错。

怎么做?

首先,我们需要确定给定变量的帧。它并不总是简单的直接前一帧。例如,我们可能有另一个函数的包装器:

 from varname import varname

def func():
  return varname()

def wrapped():
  return func()

x = wrapped()

在上面的示例中,我们必须跳过 wrapped 中的帧以到达正确的帧 x = wrapped() 以便我们能够定位 x 。参数 frameignorevarname 允许我们跳过这些中间帧。在 README 文件和包的 API 文档中查看更多详细信息。

然后我们需要解析 AST 节点来定位变量被赋值(函数调用)的位置。这并不总是一个简单的任务。有时可能会有复杂的 AST 节点,例如 x = [wrapped()] 。我们需要通过遍历 AST 树来识别正确的分配。

它有多可靠?

一旦我们确定了分配节点,它就是可靠的。

varname 都是依赖 executing 包来寻找节点。确保执行检测的节点是正确的(另请参见 this )。

它部分适用于应用其他 AST 魔法的环境,包括 pytest、ipython、macropy、birdseye、reticulate with R 等。无论是执行还是 varname 都不能 100% 地适用于这些环境。

我们需要一个包来做吗?

嗯,是的,不是的,再一次。

如果您的场景很简单,那么@juan Isaza 或@scohe001 提供的代码可能足以让您处理在直接前一帧中定义变量并且AST 节点是简单赋值的情况。您只需要返回一帧并在那里检索信息。

但是,如果场景变得复杂,或者我们需要采用不同的应用场景,你可能需要像 python-varname 这样的包来处理它们。这些场景可能包括:

  1. 当源代码不可用或 AST 节点不可访问时显示更友好的消息
  2. 跳过中间帧(允许在其他中间帧中包装或调用函数)
  3. 自动忽略来自内置函数或库的调用。例如: x = str(func())
  4. 检索赋值左侧的多个变量名
  5. 等等

f-string 怎么样?

就像@Aivar Paalberg 提供的答案一样。它绝对是快速和可靠的。但是,它不是在运行时,这意味着您必须在打印名称之前知道它是 foo 。但是使用 varname ,您不必知道变量即将到来:

 from varname import varname

def func():
  return varname()

# In external uses
x = func() # 'x'
y = func() # 'y'

最后

python-varname 不仅能够从赋值中检测变量名,而且:

  • 直接检索变量名,使用 nameof
  • 检测下一个立即属性名称,使用 will
  • 使用 argname 获取传递给函数的参数名称/源

从其文档中阅读更多内容。

但是,我想说的最后一句话是, 尽可能避免使用它。

因为您无法确保客户端代码将在源节点可用或 AST 节点可访问的环境中运行。当然,解析源代码、识别环境、检索 AST 节点并在需要时评估它们会耗费资源。

原文由 Panwen Wang 发布,翻译遵循 CC BY-SA 4.0 许可协议

使用 Python 3.8 可以简单地使用 f-string 调试功能:

 >>> foo = dict()
>>> f'{foo=}'.split('=')[0]
'foo'

这种方法的一个缺点是,为了获得 'foo' 打印,您必须自己添加 f'{foo=}' 。换句话说,您已经必须知道变量的名称。换句话说,上面的代码片段和刚才的完全一样

>>> 'foo'

请在此处查看可能适用于回答问题的其他答案。

原文由 Aivar Paalberg 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题