将变量名作为字符串获取

429

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

那么如何针对变量做同样的事情呢?与函数不同,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] 

1
对于常量,您可以使用支持检索其名称的枚举。 - Dana
2
我遇到了类似的问题。我意识到,与其将原始数据存储为一系列事物列表,不如将其存储为一系列事物字典(如果需要,您可以轻松地从字典中即时生成列表)。例如,假设您有:my_dict = {'n_jobs': ...,'users': ...}。然后,您可以使用my_dict ['n_jobs']代替n_jobs。无论如何,对我来说重要的是,我只需要一次输入“n_jobs”,无论是作为变量名称还是作为字典键中的字符串。 - Ying Zhang
变量并不是“有名字的”;在Python中,“变量”和“名称”意思相同。你可能是指(即被命名的实际对象);但事实上,值并没有“拥有”名称——它们被命名。换句话说,名称持有(保留)值。任何值都可以被命名任意次数,包括零次。 - Karl Knechtel
谢谢你的问题。我正在尝试弄清楚如何在写入结果的文件名中使用变量名(例如将名为'data'的数据框写入'data.csv'),这在具有中间结果的流水线中非常有用。使用变量名可以提供清晰且自动生成的引用。 - Rony Armon
35个回答

9

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

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))
***Input & Function call:***
my_var = 10

print_this(my_var)

***Output**:*
my_var: 10

8
>>> locals()['foo']
{}
>>> globals()['foo']
{}

如果您想编写自己的函数,可以这样做:首先检查局部变量中是否定义了变量,然后再检查全局变量。如果没有找到任何内容,可以比较id()以查看变量是否指向内存中的相同位置。
如果您的变量在类中,则可以使用className.dict.keys()或vars(self)来查看是否定义了变量。

如果名称在调用者框架中怎么办?那么你就必须像遍历栈并检查每个人的“locals”和“globals”一样傻瓜式地做事......而且如果实际上不存在任何名称,则有可能获取错误的名称。这是一项大量工作,但实际上没有任何有用的收益。 - nneonneo
整个问题看起来有些愚蠢,但如果这是他想做的事情,那么是可能的。至于检查存在性,您可以使用Globals().setdefault(var, <new object of type(var))在没有任何内容时创建一些东西。我不确定他想要什么,但也许通过学习变量是由作用域保留的,他可以找到更好的解决方案。 - blakev

6

我有一个方法,虽然不是最高效的方法...但它可行!(而且不需要任何高级模块)。

基本上,它会比较你的 变量ID全局变量ID,然后返回相匹配的名称。

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

4

我认为在Python中要做到这一点非常困难,因为你永远不会不知道你正在使用的变量的名称。所以,在他的例子中,你可以这样做:

而不是:

list_of_dicts = [n_jobs, users, queues, priorities]

dict_of_dicts = {"n_jobs" : n_jobs, "users" : users, "queues" : queues, "priorities" : priorities}

你的第一句话没有意义 - 是否多了一个“不”字? - jtlz2

4
在Python中,defclass关键字将特定的名称绑定到它们定义的对象(函数或类)。同样,由于在文件系统中被称为特定名称,因此模块也会被赋予名称。在所有三种情况下,都有一种明显的方法来为相应的对象分配“规范”名称。
然而,对于其他类型的对象,这样的规范名称可能根本不存在。例如,考虑列表中的元素。列表中的元素没有单独的名称,完全有可能在程序中仅通过使用包含列表的索引来引用它们。如果将这样一个对象列表传递给您的函数,则不可能将有意义的标识符分配给其值。
Python不会将赋值左侧的名称保存到分配的对象中,因为:
  1. 这将需要找出在多个冲突对象之间哪个名称是“规范”的,
  2. 对于从未分配给显式变量名的对象来说没有意义,
  3. 这将非常低效,
  4. 实际上没有其他任何现有语言这样做。
因此,例如使用lambda定义的函数将始终具有“名称”<lambda>,而不是特定的函数名称。
最好的方法是简单地要求调用者传递名称(可选)列表。如果键入'...','...'过于麻烦,可以接受包含逗号分隔名称列表的单个字符串(例如namedtuple)。

4

许多答案只返回一个变量名。但是如果有多个变量具有相同的值,则这种方法效果不佳。这里是Amr Sharaki的答案的变化版本,如果有多个变量具有相同的值,则返回多个结果。

def getVariableNames(variable):
    results = []
    globalVariables=globals().copy()
    for globalVariable in globalVariables:
        if id(variable) == id(globalVariables[globalVariable]):
            results.append(globalVariable)
    return results

a = 1
b = 1
getVariableNames(a)
# ['a', 'b']

3

以下是基于输入变量内容的另一种实现方式:

(它返回与输入变量匹配的第一个变量的名称,否则返回None。可以修改它以获取所有具有与输入变量相同内容的变量名称)

def retrieve_name(x, Vars=vars()):
    for k in Vars:
        if isinstance(x, type(Vars[k])):
            if x is Vars[k]:
                return k
    return None

2
每当我需要这样做,主要是在与前端通信JSON模式和常量时,我定义一个如下的类。
class Param:
    def __init__(self, name, value):
        self.name = name
        self.value = value

然后,使用名称和值定义变量。
frame_folder_count = Param({'name':'frame_folder_count', 'value':10})

现在您可以使用该对象访问名称和值。
>>> frame_folder_count.name
'frame_folder_count'

我不认为这符合问题的要求。但是无论如何,我想建议您使用 pydantic,它非常适合这种类型的用法。 - ccvhd
你的代码无法运行。 应该是以下这些代码: Param('frame_folder_count', 10) 或者 Param(**{'name':'frame_folder_count', 'value':10}) - gonzalo mr

2
>>> def varname(v, scope=None):
        d = globals() if not scope else vars(scope); return [k for k in d if d[k] == v]
...
>>> d1 = {'a': 'ape'}; d2 = {'b': 'bear'}; d3 = {'c': 'cat'}
>>> ld = [d1, d2, d3]
>>> [varname(d) for d in ld]
[['d1'], ['d2'], ['d3']]
>>> d5 = d3
>>> [varname(d) for d in ld]
[['d1'], ['d2'], ['d3', 'd5']]
>>> def varname(v, scope=None):
        d = globals() if not scope else vars(scope); return [k for k in d if d[k] is v]
...
>>> [varname(d) for d in ld]
[['d1'], ['d2'], ['d3', 'd5']]

如你所见并且在这里指出,可能会有多个变量具有相同的值或者地址,因此使用一个包装器来保持名称与数据最好。


我得到了RuntimeError: dictionary changed size during iteration - étale-cohomology
@étale-cohomology 在这里运行正常:>>> import sys;sys.version '3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)]' - Cees Timmerman

2

如果目标是帮助您跟踪变量,您可以编写一个简单的函数来标记变量并返回其值和类型。例如,假设i_f = 3.01,您将其舍入为一个整数i_n以在代码中使用,然后需要一个字符串i_s,该字符串将进入报告。

def whatis(string, x):
    print(string+' value=',repr(x),type(x))
    return string+' value='+repr(x)+repr(type(x))
i_f=3.01
i_n=int(i_f)
i_s=str(i_n)
i_l=[i_f, i_n, i_s]
i_u=(i_f, i_n, i_s)

## make report that identifies all types
report='\n'+20*'#'+'\nThis is the report:\n'
report+= whatis('i_f ',i_f)+'\n'
report+=whatis('i_n ',i_n)+'\n'
report+=whatis('i_s ',i_s)+'\n'
report+=whatis('i_l ',i_l)+'\n'
report+=whatis('i_u ',i_u)+'\n'
print(report)

这将用于调试时在窗口中打印并生成报告字符串。唯一的缺点是每次调用函数时都需要两次输入变量。
我是 Python 新手,发现这是记录编程过程中所有对象的非常有用的方法。一个缺陷是,如果 whatis() 调用在其所使用的过程之外描述的函数,则会失败。例如,int(i_f) 只有在 Python 知道 int 函数时才是有效的函数调用。您可以使用 int(i_f**2) 调用 whatis(),但如果由于某种奇怪的原因选择定义一个名为 int_squared 的函数,则必须在使用 whatis() 的过程内部声明它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接