这个线程讨论了如何在Python中以字符串的形式获取函数名:如何在Python中获得函数名作为字符串?
如何对变量做同样的处理呢?(注意Python变量没有属性
换句话说,如果我有一个变量,比如:
1 2 | foo = dict() foo['bar'] = 2 |
我正在寻找一个函数/属性,例如
1 | retrieve_name(foo) |
返回字符串
更新:
既然人们问我为什么要这么做,这里有一个例子。我想在panda中从这个列表创建一个DataFrame,其中列名由实际字典的名称给出:
1 2 | # List of dictionaries for my DataFrame list_of_dicts = [n_jobs, users, queues, priorities] |
Python中唯一具有规范名称的对象是模块、函数和类,当然,不能保证在定义了函数或类或导入了模块之后,规范名称在任何名称空间中都具有任何意义。这些名称也可以在创建对象之后修改,因此它们可能并不总是特别值得信任。
如果不递归遍历命名对象树,您想要做的是不可能的;名称是对对象的单向引用。常见的或普通的Python对象不包含对其名称的引用。想象一下,如果每个整数、每个dict、每个列表、每个布尔值都需要维护一个表示引用它的名称的字符串列表!这将是一个实现噩梦,对程序员没有什么好处。
即使变量值不指向名称,您也可以访问每个已分配变量及其值的列表,所以只有一个人建议循环访问该列表以查找您的var名称,这让我很吃惊。
有人在回答中提到,您可能需要遍历堆栈并检查每个人的局部变量和全局变量来找到
我的解释可能有点太啰嗦了(也许我应该少用"foo"这个词),但这是它在代码中的样子(注意,如果有多个变量分配给相同的值,您将得到这两个变量的名称):
1 2 3 4 5 6 7 8 9 | 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) |
如果你从另一个函数调用这个函数,比如:
1 2 3 4 | def foo(bar): return retrieve_name(bar) foo(baz) |
您想要的是
这里有一个例子:ideone
我不相信这是可能的。考虑下面的例子:
1 2 3 4 5 6 | >>> a = [] >>> b = a >>> id(a) 140031712435664 >>> id(b) 140031712435664 |
在python3中,这个函数将得到堆栈中最外层的名称:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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] |
它在代码的任何地方都是有用的。遍历反向堆栈,寻找第一个匹配项。
1 2 | def name(**variables): return [x for x in variables] |
它的用法是这样的:
1 | name(variable=variable) |
这是一种方法。我不推荐任何重要的东西,因为它会很脆。但这是可以做到的。
创建一个函数,使用
1 2 3 4 | x = 'foo' y = 'bar' d = autodict(x, y) print d |
将:
1 | {'x': 'foo', 'y': 'bar'} |
检查源代码本身比在
无论如何,代码如下:
1 2 3 4 5 6 7 8 9 10 11 | def autodict(*args): get_rid_of = ['autodict(', ',', ')', ' '] 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 |
操作发生在
这种魔术的明显缺点是,它对源代码的结构进行了假设。当然,如果它在解释器中运行,它根本就不能工作。
我写了包魔法来强力地做这种魔法。你可以写:
1 2 3 | from sorcery import dict_of columns = dict_of(n_jobs, users, queues, priorities) |
并将其传递给dataframe构造函数。等价于:
1 | columns = dict(n_jobs=n_jobs, users=users, queues=queues, priorities=priorities) |
在Python中,
然而,对于其他类型的对象,这样的规范名称可能根本不存在。例如,考虑列表的元素。列表中的元素没有单独命名,在程序中引用它们的唯一方法完全可能是使用包含列表的列表索引。如果将这样的对象列表传递到函数中,您可能无法为这些值分配有意义的标识符。
Python不会将赋值左侧的名称保存到赋值对象中,因为:
它需要在多个相互冲突的对象中找出哪个名称是"规范的",对于从未赋值给显式变量名的对象,这将是非常低效的,实际上,没有其他语言能做到这一点。因此,例如,使用
最好的方法是让调用者传入一个(可选的)名称列表。如果输入
我认为在Python中做到这一点非常困难,因为一个简单的事实是,您永远不会不知道正在使用的变量的名称。所以,在他的例子中,你可以这样做:
而不是:
1 2 3 | list_of_dicts = [n_jobs, users, queues, priorities] dict_of_dicts = {"n_jobs" : n_jobs,"users" : users,"queues" : queues,"priorities" : priorities} |
1 2 3 4 | >>> locals()['foo'] {} >>> globals()['foo'] {} |
如果您想编写自己的函数,可以这样做:检查在局部变量中定义的变量,然后检查全局变量。如果没有找到任何内容,可以对id()进行比较,看看变量是否指向内存中的相同位置。
如果变量在类中,可以使用className.dict.keys()或vars(self)来查看是否定义了变量。
这个函数将打印变量名及其值:
1 2 3 | 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)) |
1
2
3
4
5
6
7 ***Input & Function call:***
my_var = 10
print_this(my_var)
***Output**:*
my_var: 10
另一种基于输入变量内容的方法是:
(它返回与输入变量匹配的第一个变量的名称,否则为None。可以修改它以获得与输入变量内容相同的所有变量名)
1 2 3 4 5 6 | def retrieve_name(x, Vars=vars()): for k in Vars: if type(x) == type(Vars[k]): if x is Vars[k]: return k return None |
如果目标是帮助跟踪变量,可以编写一个简单的函数来标记变量并返回其值和类型。例如,假设i_f=3.01,将其四舍五入为整数i_n,以便在代码中使用,然后需要一个字符串i_s,该字符串将进入报表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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=' '+20*'#'+' This is the report: ' report+= whatis('i_f ',i_f)+' ' report+=whatis('i_n ',i_n)+' ' report+=whatis('i_s ',i_s)+' ' report+=whatis('i_l ',i_l)+' ' report+=whatis('i_u ',i_u)+' ' print(report) |
这将在每次调用时打印到窗口进行调试,并为书面报告生成一个字符串。唯一的缺点是每次调用函数时都必须两次键入变量。
我是一个Python新手,发现这是一种非常有用的方法,可以记录我在编写程序和尝试处理Python中的所有对象时所做的工作。一个缺陷是,如果whatis()调用了在使用它的过程之外描述的函数,那么它就会失败。例如,int(i_f)是一个有效的函数调用,因为Python知道int函数。您可以使用int(i_f**2)调用whatis(),但是如果出于某种奇怪的原因,您选择定义一个名为int_squared的函数,那么它必须在使用whatis()的过程中声明。
我试图从inspect局部变量获得name,但它不能处理像[1],b.val这样的var。在此之后,我有了一个新想法——从代码中获取var name,然后尝试使用succ!下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 | #direct get from called function code def retrieve_name_ex(var): stacks = inspect.stack() try: func = stacks[0].function code = stacks[1].code_context[0] s = code.index(func) s = code.index("(", s + len(func)) + 1 e = code.index(")", s) return code[s:e].strip() except: return"" |
对于常量,可以使用enum,它支持检索其名称。