将变量的名称作为字符串

Ame*_*ina 112 python

这个主题讨论如何在Python中将函数的名称作为字符串获取:如何在Python中将 函数名称作为字符串获取?

如何为变量做同样的事情?(注意Python变量没有属性__name__,至少在Python中retrieve_name)

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

foo = dict()
foo['bar'] = 2
Run Code Online (Sandbox Code Playgroud)

我正在寻找一个函数/属性,例如'foo':

retrieve_name(foo) 
Run Code Online (Sandbox Code Playgroud)

返回字符串 __name__

更新:

既然人们在问我为什么要这样做,这里就是一个例子.我想在此列表中在Pandas中创建一个DataFrame,其中列名 由实际字典的名称给出:

# List of dictionaries for my DataFrame
list_of_dicts = [n_jobs, users, queues, priorities]
Run Code Online (Sandbox Code Playgroud)

Aiv*_*erg 176

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

>>> foo = dict()
>>> f'{foo=}'.split('=')[0]
'foo' 
Run Code Online (Sandbox Code Playgroud)

  • 许多人似乎忽略了这一点(除了@Hugo),为了能够编写`f'{foo=}'.split('=')[0]`,你显然**已经知道了多变的**。 (17认同)
  • 这到底有什么用呢?只有手动写入“foo”才能得到结果“foo”。它不能解决OP问题。 (15认同)
  • 所有复杂的“这是不可能的”答案,然后就是这个 - 字面上是一行,而不导入任何东西。感谢您分享这个,好先生! (8认同)
  • @Hugo 我正在寻找这个以节省打字。我有许多 pandas 数据框要导出到 .xlsx,其中数据框的名称是输出 excel 文件中工作表的名称,如下所示: vel.to_excel(writer,sheet_name='vel'), vol.to_excel( writer,sheet_name ='vol'),area.to_excel(writer,sheet_name ='area')。它将节省打字,减少出错的机会,并使代码更具可读性,将数据帧放入列表中,然后在 for 循环中进行导出 - for df in df_list: df.to_excel(writer,sheet_name=df.名称)或类似的东西。 (8认同)
  • 它确实有助于检测变量名称的更改。并且 IDE 会将旧值显示为错误。例如,如果有一天 'foo' 更改为 'boo',像 `"foo is a var"` 这样的字符串将保留旧名称,这不好,而 `f"{foo=}".split( '=')[0] + "is a var"` 将会出现编译错误,并迫使开发人员将 `{foo=}` 更改为 `{boo=}`。 (7认同)
  • 好的 !如果你想获取属性的名称而不是对象,你可以 `f'{foo=}'.split('=')[0].split('.')[-1]` (4认同)
  • @AivarPaalberg 仍然 f'{foo=}'.split('=')[0] 从来没有用。在什么可能的情况下它有用?以这种速度,表达式 "i|have|to|manually|type|foo".split('|')[-1] 对于获取字符串“foo”同样有用。 (4认同)
  • @Artur 公平地说,这个功能是 3.8+,所以这些答案在发布时可能是有效的。 (2认同)
  • 它确实在回答时解决了OP问题。这个问题随着时间的推移而“演变”,存在哪个标题与内容一致的问题。然后是 pandas 部分的更新,明确标记为“更新”。然后它被编辑并成为问题的一部分,这被遗漏了:“我正在寻找一个函数/属性,例如retrieve_name:retrieve_name(foo),它返回字符串'foo'”。现在任何“聚会迟到”的人都会想知道这些答案到底是什么...... (2认同)
  • @martineau 是的,但在该表达式中, `f'{foo=}'` foo 现在已连接到任何重构工具...而将名称 foo 硬编码到字符串中则无法在没有手动操作的情况下自动重构。一般来说,这就是您需要反思的**原因**。当我们最初编写变量名时,我们总是知道变量名,除非你做了一些尴尬的事情。 (2认同)

kin*_*all 97

Python中具有规范名称的唯一对象是模块,函数和类,当然,在定义函数或类或导入模块之后,无法保证此规范名称在任何命名空间中具有任何含义.创建对象后也可以修改这些名称,因此它们可能并不总是特别值得信赖.

如果没有递归地遍历命名对象树,你不可能做什么.名称是对象的单向引用.常见或花园种类的Python对象不包含对其名称的引用.想象一下,如果每个整数,每个字典,每个列表,每个布尔值都需要维护表示引用它的名称的字符串列表!这将是一个实现噩梦,对程序员几乎没有好处.

  • 谢谢.但是为什么Python会为函数执行此操作呢?(即一种类型的Python对象) (7认同)
  • 您的声明"它根本不可能"是假的,如@ scohe001 [显示](http://stackoverflow.com/a/18425523/623735).Python的变量名称数据库就像任何其他关系数据库一样,您可以始终在"反向"中搜索相关对象,并为任何给定变量返回第一个找到的或整个有效变量名称集. (6认同)
  • @hobs从技术上来说您是正确的……最好的一种正确的选择。但是,实际上,一个对象的潜在名称太多了,以致于比试图获得它们的价值更大。 (2认同)
  • @kindall,如果你的“值得”阈值是 O(1),我想你是对的。scohe001 的循环时间复杂度为 O(N)。 (2认同)
  • @hobs 好吧,对象可以是列表或字典或其他容器的成员,也可以是对象属性...因此在这种情况下,您也需要找到包含对象的名称...可能被另一个对象包含...所以您可能需要递归查找名称,直到找到当前代码可以访问的对象。编写和调试很多容易出错的代码似乎并没有太大好处。 (2认同)

sco*_*001 72

即使变量值没有指向名称,您也可以访问每个已分配变量及其值的列表,所以我很惊讶只有一个人建议在那里循环查找您的var名称.

有人在回答中提到你可能需要遍历堆栈并检查每个人的locals和globals才能找到foo,但如果foo在你调用这个retrieve_name函数的范围内分配,你可以使用inspect's current frame来获取所有这些局部变量.我的解释可能有点过于冗长(也许我应该使用"foo"更少的单词),但这里的代码看起来如何(请注意,如果有多个变量分配给相同的值,你会获取这两个变量名称):

import inspect

x,y,z = 1,2,3

def retrieve_name(var):
    callers_local_vars = inspect.currentframe().f_back.f_locals.items()
    return [var_name for var_name, var_val in callers_local_vars if var_val is var]

print retrieve_name(y)
Run Code Online (Sandbox Code Playgroud)

  • 这样做.那说:它根本不应该完成:) (9认同)
  • 这不行.原子变量的名称不是属性.所以如果你有`a,b = 2,2',`retrieve_name(a)`和`retrieve_name(b)`都将返回`['a','b']`或`['b','a "]` (4认同)
  • @tomas实际上,这是cPython优化的实现细节,其中255以下的整数基本上是单例,因此分配这些值的任何变量都将有效地返回“true”以进行“is”比较 (3认同)
  • 有没有一个mod可以获取传递的变量的名称?例如,“def foo(bar):retrieve_name(bar)”将始终返回 bar,但如果您希望“foo(baz)”返回“baz”而不是“bar”,该怎么办? (2认同)
  • @SumNeuron,您只需要修改分配“callers_local_vars”的行即可向后移动一个范围,这样它将查看调用“foo”的任何内容的范围。将行更改为“inspect.currentframe().f_back.f_back.f_locals.items()”(注意额外的“f_back”)。 (2认同)

Pan*_*ang 60

使用该python-varname包,您可以轻松检索变量的名称

https://github.com/pwwang/python-varname

由于v0.6.0,你的情况,你可以这样做:

from varname.helpers import Wrapper

foo = Wrapper(dict())

# foo.name == 'foo'
# foo.value == {}
foo.value['bar'] = 2
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

更新:07/08/2021

要获取数据框的名称,您可以使用argname2(从 v0.7.1 开始)编写自己的包装器:

>>> # argname is superseded by argname2
>>> from varname import argname2 
>>> from pandas import DataFrame
>>> 
>>> df1 = DataFrame(dict(a=[1]))
>>> df2 = DataFrame(dict(b=[1]))
>>> 
>>> def handle_dfs(*dfs):
...     names = argname2('*dfs')
...     for name, df in zip(names, dfs):
...         df.__dfname__ = name
...     
...     for df in dfs:
...         print(f'name: {df.__dfname__}, value: {df}')
... 
>>> handle_dfs(df1, df2)
name: df1, value:    a
0  1
name: df2, value:    b
0  1
Run Code Online (Sandbox Code Playgroud)

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

  • 好吧,终于明白了!使用以下`pip3 install python-varname==0.1.5`安装;使用“from varname import nameof”导入 (2认同)
  • @ted930511 我不介意是否有人将其添加到任何 conda 频道。欢迎成为该项目的贡献者,将其添加到频道并保持最新状态。 (2认同)

jua*_*aza 25

在python3上,此函数将获取堆栈中最外层的名称:

import inspect


def retrieve_name(var):
        """
        Gets the name of var. Does it from the out most frame inner-wards.
        :param var: variable to get name from.
        :return: string
        """
        for fi in reversed(inspect.stack()):
            names = [var_name for var_name, var_val in fi.frame.f_locals.items() if var_val is var]
            if len(names) > 0:
                return names[0]
Run Code Online (Sandbox Code Playgroud)

它在代码的任何地方都很有用.遍历反向堆栈寻找第一场比赛.


kor*_*nce 22

我不相信这是可能的.请考虑以下示例:

>>> a = []
>>> b = a
>>> id(a)
140031712435664
>>> id(b)
140031712435664
Run Code Online (Sandbox Code Playgroud)

ab指向同一个对象,但对象无法知道指向它哪些变量.

  • 通过扩展[引用计数](https://en.m.wikipedia.org/wiki/Reference_counting),可以肯定地建立这种关系。这个[answer](/sf/answers/3594359051/)(以及其他一些)甚至提供了一个实现。 (2认同)

fdv*_*ges 13

def name(**variables):
    return [x for x in variables]
Run Code Online (Sandbox Code Playgroud)

它的使用方式如下:

name(variable=variable)
Run Code Online (Sandbox Code Playgroud)

  • 这不会返回所需变量的名称,而是返回"变量".例如 - 使用`name(variable = variable)`将输出`['variable']`,并使用`name(variable = another_variable)`将*not*output` ['another_variable']`而不是'['变量' "]`. (11认同)
  • 这只是 varname = 'variable' 的向后写法。name 函数中的第一个“变量”不是变量,而是关键字名称。相当于写``lambda name, var: name``` (2认同)

Ans*_*dey 10

>> my_var = 5
>> my_var_name = [ k for k,v in locals().items() if v == my_var][0]
>> my_var_name 
'my_var'
Run Code Online (Sandbox Code Playgroud)

如果 myvar 指向另一个变量时出现错误,请尝试此操作(@mherzog 建议)-

 >> my_var = 5
>> my_var_name = [ k for k,v in locals().items() if v is my_var][0]
>> my_var_name 
'my_var'
Run Code Online (Sandbox Code Playgroud)

locals() - 返回一个包含当前作用域局部变量的字典。通过遍历这个字典,我们可以检查具有等于定义变量的值的键,只需提取键就会为我们提供字符串格式的变量文本。

来自(稍作更改后) https://www.tutorialspoint.com/How-to-get-a-variable-name-as-a-string-in-Python


小智 8

这是一种方法.我不建议任何重要的东西,因为它会很脆弱.但这是可以完成的.

创建一个使用该inspect模块查找调用它的源代码的函数.然后,您可以解析源代码以标识要检索的变量名称.例如,这是一个调用的函数autodict,它接受一个变量列表并返回一个字典映射变量名称到它们的值.例如:

x = 'foo'
y = 'bar'
d = autodict(x, y)
print d
Run Code Online (Sandbox Code Playgroud)

会给:

{'x': 'foo', 'y': 'bar'}
Run Code Online (Sandbox Code Playgroud)

检查源代码本身比搜索locals()或更好,globals()因为后一种方法不能告诉你哪些变量是你想要的.

无论如何,这是代码:

def autodict(*args):
    get_rid_of = ['autodict(', ',', ')', '\n']
    calling_code = inspect.getouterframes(inspect.currentframe())[1][4][0]
    calling_code = calling_code[calling_code.index('autodict'):]
    for garbage in get_rid_of:
        calling_code = calling_code.replace(garbage, '')
    var_names, var_values = calling_code.split(), args
    dyn_dict = {var_name: var_value for var_name, var_value in
                zip(var_names, var_values)}
    return dyn_dict
Run Code Online (Sandbox Code Playgroud)

该操作发生在with行中inspect.getouterframes,它返回调用的代码中的字符串autodict.

这种魔力的明显缺点是它对源代码的结构做出了假设.当然,如果它在解释器中运行,它根本不起作用.

  • 你好,我只有三个问题: 1.为什么是“1”?2.为什么是“4”?3、为什么是“0”?:) (2认同)

Ale*_*all 7

我编写了包裹法术来稳健地执行这种魔术。你可以写:

from sorcery import dict_of

columns = dict_of(n_jobs, users, queues, priorities)
Run Code Online (Sandbox Code Playgroud)

并将其传递给数据框构造函数。等效于:

columns = dict(n_jobs=n_jobs, users=users, queues=queues, priorities=priorities)
Run Code Online (Sandbox Code Playgroud)


bla*_*kev 5

>>> locals()['foo']
{}
>>> globals()['foo']
{}
Run Code Online (Sandbox Code Playgroud)

如果要编写自己的函数,可以这样做,以便可以检查在本地变量中定义的变量,然后检查全局变量。如果未找到任何内容,则可以在id()上进行比较,以查看变量是否指向内存中的相同位置。

如果变量在类中,则可以使用className。dict .keys()或vars(self)来查看变量是否已定义。


iDi*_*lip 5

此函数将打印变量名称及其值:

import inspect

def print_this(var):
    callers_local_vars = inspect.currentframe().f_back.f_locals.items()
    print(str([k for k, v in callers_local_vars if v is var][0])+': '+str(var))
Run Code Online (Sandbox Code Playgroud)
***Input & Function call:***
my_var = 10

print_this(my_var)

***Output**:*
my_var: 10
Run Code Online (Sandbox Code Playgroud)


Amr*_*aki 5

我有一个方法,虽然不是最有效的……但它有效!(并且它不涉及任何花哨的模块)。

基本上它会将您的变量 IDglobals() 变量 IDs 进行比较,然后返回匹配项的名称。

def getVariableName(variable, globalVariables=globals().copy()):
    """ Get Variable Name as String by comparing its ID to globals() Variables' IDs

        args:
            variable(var): Variable to find name for (Obviously this variable has to exist)

        kwargs:
            globalVariables(dict): Copy of the globals() dict (Adding to Kwargs allows this function to work properly when imported from another .py)
    """
    for globalVariable in globalVariables:
        if id(variable) == id(globalVariables[globalVariable]): # If our Variable's ID matches this Global Variable's ID...
            return globalVariable # Return its name from the Globals() dict
Run Code Online (Sandbox Code Playgroud)