简述
使用python-varname
中的Wrapper
辅助工具:
from varname.helpers import Wrapper
foo = Wrapper(dict())
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]
我是
python-varname
包的作者。如果您有任何问题,可以告诉我,或者您可以在Github上提交问题。
详细回答
这真的可能吗?
是和否。
我们需要调用一个函数来访问前一帧以检索变量名称,因此我们正在运行时检索变量名称,所以我们需要一个Wrapper
。在该函数中,我们在运行时解析前一帧中的源代码/AST节点以获取确切的变量名称。
但是,前面的源代码/AST节点并不总是可用的,或者它们可能会被其他环境修改(例如:pytest
的assert
语句)。一个简单的例子是通过exec()
运行的代码。即使我们仍然能够从字节码中检索一些信息,但这需要太多的努力,而且容易出错。
如何做到这一点?
首先,我们需要确定变量所在的帧。这通常不仅仅是直接前一帧。例如,我们可能还有另一个函数的包装器:
from varname import varname
def func():
return varname()
def wrapped():
return func()
x = wrapped()
在上面的例子中,我们需要跳过
wrapped
内部的帧才能找到正确的帧
x = wrapped()
,以便我们能够定位
x
。
varname
的参数
frame
和
ignore
允许我们跳过其中一些中间帧。有关更多详细信息,请参见包的README文件和API文档。
然后,我们需要解析AST节点以定位变量被赋值(函数调用)的位置。它并不总是一个简单的赋值。有时可能会有复杂的AST节点,例如
x = [wrapped()]
。我们需要通过遍历AST树来识别正确的赋值。
它有多可靠?
一旦我们确定了赋值节点,它就是可靠的。
varname
完全依赖于
executing
包来查找节点。确保执行检测到的节点是正确的(也请参见
this)。
它部分地适用于其他应用了AST魔法的环境,包括pytest、ipython、macropy、birdseye、reticulate with R等。executing和varname都无法100%地与这些环境配合使用。
我们需要一个包来完成它吗?
嗯,是的和不是的。
如果您的情景比较简单,@juan Isaza或@scohe001提供的代码可能已经足够让您处理定义在直接前一帧且AST节点是简单赋值的情况。您只需要回退一帧并检索那里的信息即可。
然而,如果情景变得复杂,或者我们需要采用不同的应用场景,您可能需要类似
python-varname
这样的包来处理它们。这些情景可能包括:
- 在源代码不可用或AST节点不可访问时呈现更友好的消息
- 跳过中间帧(允许将函数包装或调用在其他中间帧中)
- 自动忽略内置函数或库的调用。例如:
x = str(func())
- 检索赋值左侧的多个变量名称
- 等等。
关于
f-string
呢?
就像@Aivar Paalberg提供的答案一样。它确实快速可靠。但是,它不是在运行时,这意味着您必须在打印名称之前知道它是
foo
。但是使用
varname
,您不必知道该变量是什么:
from varname import varname
def func():
return varname()
x = func()
y = func()
最后
python-varname
不仅可以从赋值语句中检测变量名,还可以:
- 直接使用
nameof
检索变量名
- 使用
will
检测下一个属性名
- 使用
argname
获取传递给函数的参数名称/源
请查看其文档以获取更多信息。
然而,我想说的最后一句话是,尽可能避免使用它。
因为您无法确定客户端代码是否在可用源节点或 AST 节点可访问的环境中运行。当然,在需要时解析源代码、识别环境、检索 AST 节点并评估它们也需要资源。