将变量名作为字符串获取

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个回答

2
也许这对你有帮助:
def Retriever(bar):
    return (list(globals().keys()))[list(map(lambda x: id(x), list(globals().values()))).index(id(bar))]

该函数遍历全局作用域中值的ID列表(可编辑命名空间),根据其ID查找所需变量或函数的索引,然后基于获取的索引从全局名称列表中返回名称。


看起来非常可怕,但它能够工作,并且速度相当快。对于一个变量和函数,每次循环的时间为4.41 us +/- 1.2 us(7次运行的平均值+标准偏差,每次循环100000次)。随着定义的变量数量增加,运行时间会缓慢增加。在iPython中定义了103个变量时,需要21us。也许不适用于非常大的环境,但也许有一种优化的方法...? - Kamikaze Rusher
好的,这似乎存在一些缺点。例如,假设有两个变量被赋值为0,并分配给一个元组,即t = (var1, var2)。虽然可以使用它来获取分配给元组槽的变量名,但是var2的槽将返回var1的变量名。我认为这是因为常量被预先生成并分配到内存中的一个槽位作为优化技术,因此反向查找将指向分配该值的最早存在的变量。因此,由于不准确性问题,我不建议在大型项目中使用它。 - Kamikaze Rusher

1
您可以将变量作为kwargs获取,并将其作为字符串返回:
var=2
def getVarName(**kwargs):
    return list(kwargs.keys())[0]

print (getVarName(var = var))

注意:变量名必须与自身相等。

这个答案太被低估了!可以很好地处理 var=Nonevar=''var=_ 等情况。我已经尝试过使用 globals()、.repr 和 id() 等方法,但是在一些奇怪的测试变量中并不起作用。回答得非常好! - user319436
无法工作,s。print([getVarName(v=v) for v in [v1, v2, v3]]) - Jaja

1

如果有两个变量具有相同的值,之前的一些情况可能会失败。因此,提醒一下是很方便的:

定义函数:

# Variable to string of variable name

def var_name(variable,i=0):

  results = []
  for name in globals():   
     if eval(name) == variable:
       results.append(name)

  if len(results) > 1:
    print('Warning:' )
    print('   var_name() has found',len(results), 'possible outcomes.')
    print('   Please choose the suitable parameter "i". Where "i" is the index')
    print('   that matches your choice from the list below.')
    print('  ',results) ; print('')

  return results[i]

使用:

var_1 = 10
var_name(var_1) # Output will be "var_1"

如果您有两个变量具有相同的值,例如var_1 = 8var_2 = 8,那么会出现一个警告。请注意保留HTML标签。
var_1 = 8
var_2 = 8
var_name(var_2)  # Output will be "var_1" too but Warning will appear

1
如何对变量执行相同的操作?与函数不同,Python 变量没有 __name__ 属性。
问题出现是因为您对术语、语义或两者都感到困惑。
“变量”不属于“函数”的同一类别。一个“变量”不是在代码运行时占用内存的东西。它只是一个存在于您的源代码中的名称,这样当您编写代码时,就可以解释您正在谈论哪个事物。Python 在源代码中使用名称来引用(即给值命名)。 (在许多语言中,变量更像是将值存储在特定内存位置的名称。但是 Python 的名称实际上是所涉及事物的名称。)
在Python中,函数是一个值。(在某些语言中,情况并非如此;虽然有用于表示实际可执行代码的字节内存,但它不是程序逻辑直接交互的离散内存块。)在Python中,每个值都是一个对象,这意味着您可以自由地为其分配名称,将其作为参数传递,从函数返回等等。(在许多语言中,情况并非如此。)Python中的对象具有属性,这是使用.语法访问的内容。Python中的函数具有__name__属性,在创建函数时分配。特别地,当执行def语句时(在大多数语言中,函数的创建方式完全不同),在def之后出现的名称被用作__name__属性的值,并且也是独立的变量名,该变量名将被分配到函数对象。

但是,大多数对象都没有这样的属性。

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

那就是这样子的:你并没有以你想的那种方式“拥有”这个变量。你拥有的只是那个被这个变量所命名的对象。其他任何东西都依赖于偶然存储在其他对象中的信息,比如包含函数的locals()。但最好还是自己存储这些信息。不要依赖变量名称来携带信息,而是显式地构建字符串名称与对象本身之间的映射关系

1

以下方法不会返回变量的名称,但如果变量在全局作用域中可用,您可以使用此方法轻松创建数据框架。

class CustomDict(dict):
    def __add__(self, other):
        return CustomDict({**self, **other})

class GlobalBase(type):
    def __getattr__(cls, key):
        return CustomDict({key: globals()[key]})

    def __getitem__(cls, keys):
        return CustomDict({key: globals()[key] for key in keys})

class G(metaclass=GlobalBase):
    pass

x, y, z = 0, 1, 2

print('method 1:', G['x', 'y', 'z']) # Outcome: method 1: {'x': 0, 'y': 1, 'z': 2}
print('method 2:', G.x + G.y + G.z) # Outcome: method 2: {'x': 0, 'y': 1, 'z': 2}

A = [0, 1]
B = [1, 2]
pd.DataFrame(G.A + G.B) # It will return a data frame with A and B columns

0

只要实例变量是类的属性,就完全可以获取其名称。

我从Brett Slatkin的《Effective Python》中得到了这个方法。希望能帮助到大家:

该类必须实现“描述符协议”的getsetset_name方法。

当我运行它时,它起作用了:

class FieldThatKnowsItsName():
    def __init__(self):
        self.name = None
        self._value= None
        self.owner = None
 
    def __set_name__(self, owner, name):
        self.name = name
        self.owner = owner
        self.owner.fields[self.name] = self

    def __get__(self, instance, instance_type):
        return self

    def __set__(self, instance, value):
        self = value

class SuperTable:
    fields = {}
    field_1=FieldThatKnowsItsName()
    field_2=FieldThatKnowsItsName()

table = SuperTable()
print(table.field_1.name)
print(table.field_2.name)

然后,您可以根据需要添加方法或扩展数据类型。

作为奖励,set_name(self, owner, name) dunder 还会传递父实例,因此 Field 类实例可以向父级注册自己。

我从 Brett Slatkin 的《Effective Python》中学到了这个。花了一些时间才弄明白如何实现。


0
你可以通过在函数内部更新'globals'变量并跟踪块内定义的变量,动态获取变量的名称。以下是一个示例:
keys1 = set(globals().keys())
a = 1
b = 1 
c = 2
d = (a,b)
keys2 = set(globals().keys())
vals = [a,b,c,d]
new_keys = [ key for key in new_keys if globals()[key] in vals]
new_keys = keys2.difference(keys1)
new_keys = [ key for key in new_keys if globals()[key] in vals]
print(new_keys)
myg = globals()
for key in new_keys:
    myg[key] = (val,key) # now the variable points also to the key
for key in new_keys:
    myg[key] = (globals()[key],key)
# now the variable points also to the key
print(a,b,c,d)

一旦您获得了变量的名称,您可以再次修改全局变量以恢复变量的值。这可以使用上下文管理器完成。

请注意,您不能修改globals()中的所有变量,因为这可能会导致一些问题,这就是我使用在globals中定义的变量进行限制的原因。

此外,完整的解决方案不一定是线程安全的,并且必须在重新分配之前释放在上下文中定义的变量,以便“new_keys”至少包含新变量的名称。


0
from __future__ import annotations
import inspect
import pandas as pd


# 변수 이름 가져오기
def getVariableName(variable: str) -> (str | Exception):
    callers_local_vars = inspect.currentframe().f_back.f_locals.items()

    df = pd.DataFrame(callers_local_vars)
    df.columns = ['함수명', '값']

    try:
        return df[(df['값'] == variable)].iloc[0]['함수명']
    except Exception as e:
        return e


test = 'Hello World!'

if __name__ == '__main__':
    print(getVariableName(test))

0

如果您已经有一个数据框列表,正如评论中所述,那么将该列表转换为字符串列表就很容易了。 不要从变量列表转换为字符串,而是反过来,使用内置的exec函数和f-strings将字符串列表转换为变量(假设变量已经被赋值,即velvolarea是现有pandas数据框的变量名):

如果:

dfstr_list = [
              'vel',
              'vol',
              'area'
             ]

这个程序通过遍历列表,使用每个数据帧写入 Excel,并定义工作表名称:

for dfstr in dfstr_list:
    if dfstr in globals():
        exec(f"{dfstr}.to_excel(writer,sheet_name=dfstr)")

0

当从变量的值中查找变量名称时,可能会有几个变量等于相同的值,例如var1 = 'hello'和var2 = 'hello'。

我的解决方案:

def find_var_name(val):

    dict_list = []
    global_dict = dict(globals())

    for k, v in global_dict.items():
        dict_list.append([k, v])
   
    return [item[0] for item in dict_list if item[1] == val]

var1 = 'hello'
var2 = 'hello'
find_var_name('hello')

输出

['var1', 'var2']

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