Python获取函数调用外部的变量名
让我们有下面的代码Python获取函数调用外部的变量名,python,python-3.x,Python,Python 3.x,让我们有下面的代码 def f(a, b, c): import inspect stack = inspect.stack() # How to know the original variable names i.e 'arg1, 'arg2' & 'arg3' arg1 = arg2 = arg3 = None f(arg1, arg2, arg3) 现在我想知道用于调用f() 如中所示,我们可以使用inspect模块获取帧。但是在这种情况下,它没有帮
def f(a, b, c):
import inspect
stack = inspect.stack()
# How to know the original variable names i.e 'arg1, 'arg2' & 'arg3'
arg1 = arg2 = arg3 = None
f(arg1, arg2, arg3)
现在我想知道用于调用f()
如中所示,我们可以使用inspect
模块获取帧。但是在这种情况下,它没有帮助,因为我必须解析“f(arg1,arg2,arg3)\n”
,这将是一个混乱的过程
有其他选择吗?这似乎是您正在尝试的一种粗略的语法解决方法。改为传递一个
dict
或一个kwargs
使用dict
,字典使用键值对:
def f(d):
print(d)
print(d.keys())
f({'arg1': None, 'arg2': None, 'arg3': None})
# Output
# {'arg1': None, 'arg2': None, 'arg3': None}
# dict_keys(['arg1', 'arg2', 'arg3'])
使用**kwargs
,它接受数量可变的关键字参数:
def f(**kwargs):
d = dict(kwargs) # cast into a dictionary
print(d)
print(d.keys())
# or use it directly
for arg in kwargs:
print(arg, '=', kwargs[arg])
f(arg1 = None, arg2 = None, arg3 = None)
# Output
# {'arg1': None, 'arg2': None, 'arg3': None}
# dict_keys(['arg1', 'arg2', 'arg3'])
# arg1 = None
# arg2 = None
# arg3 = None
然后,您可以使用字典的键查找参数名称。这似乎是您正在尝试的粗略语法解决方法。改为传递一个
dict
或一个kwargs
使用dict
,字典使用键值对:
def f(d):
print(d)
print(d.keys())
f({'arg1': None, 'arg2': None, 'arg3': None})
# Output
# {'arg1': None, 'arg2': None, 'arg3': None}
# dict_keys(['arg1', 'arg2', 'arg3'])
使用**kwargs
,它接受数量可变的关键字参数:
def f(**kwargs):
d = dict(kwargs) # cast into a dictionary
print(d)
print(d.keys())
# or use it directly
for arg in kwargs:
print(arg, '=', kwargs[arg])
f(arg1 = None, arg2 = None, arg3 = None)
# Output
# {'arg1': None, 'arg2': None, 'arg3': None}
# dict_keys(['arg1', 'arg2', 'arg3'])
# arg1 = None
# arg2 = None
# arg3 = None
然后可以使用字典的键查找参数名。这可能不可取,但至少在某种程度上可以通过解析调用方的字节码来实现。具体地说,您需要使用例如frame=inspect.stack()[-2][0]
获取调用方的堆栈帧,查看frame.f_code.co_code
中的原始字节码,查看frame.f_lasti
中执行的最后一条指令的索引-这将是导致调用函数的CALL_函数
操作码。(只要您还没有返回-之后,f_lasti
将随着调用方框架中执行的进行而更新)
现在,解析字节码一点也不难,至少在你开始重构控制流之前(如果你能假设被调用的函数不使用三元运算符,和
或或
),但这本身并不难,如果你以前没有做过类似的事情(比如,玩弄事物的内部结构,或者其他一些“低级”的东西),那可能是一座需要攀登的高山,这取决于人;这可能不是一个快速的练习
那么,并发症呢?首先,字节码是CPython的一个实现细节,所以这在诸如Jython之类的替代解释器中不起作用。此外,字节码可以从CPython版本更改到下一个版本,因此大多数CPython版本将需要稍微不同的解析代码
早些时候,我也说过“在某种程度上”——这是什么意思?嗯,我已经提到处理条件可能很难。此外,如果值未存储在变量中,则无法获取变量名!例如,您的函数可能被调用为f(1,2,3)
,f(mylist[0],mydict['asd'],myfunc())
或f(*make_args(),**make_kwargs())
——在前两种情况下,您可能只需要确定与每个参数对应的表达式而不是变量名。。。但最后一个案子呢?任何一个表达式都可能对应多个参数,并且您不知道哪个参数来自哪个表达式!一般来说,你也不知道这一点——因为值来自函数调用,你不能在不再次调用函数的情况下查看它们——没有任何东西可以保证函数不会有副作用,或者它们会再次返回相同的值。或者,就像前面的条件句一样,您可以假设调用者没有执行任何操作
简言之,这是可能的——在某种程度上——甚至可能并不特别困难——至少如果你知道自己在做什么的话——但这肯定不会是简单、快速、普遍或可取的
编辑:如果出于某种原因——毕竟——你仍然觉得出于某种原因(或者如果你只是好奇它会是什么样子),那么这里有一个非常有限的概念证明可以让你开始。(仅适用于CPython3.4,仅适用于位置参数,仅适用于局部变量。此外,对于可能不建议这样做,但至少在某种程度上可以通过解析调用方的字节码来实现。具体来说,您可能希望使用例如
frame=inspect.stack()[-2][0]获取调用方的堆栈帧。)
,查看frame.f\u code.co\u code
中的原始字节码和frame.f\u lasti
中执行的最后一条指令的索引-这将是导致调用函数的调用函数
操作码。(只要您还没有返回-之后,f_lasti
将随着调用方框架中执行的进行而更新)
现在,解析字节码一点也不难,至少在你开始重构控制流之前是这样(如果你能假设被调用的函数不使用三元运算符,和
或或
,在函数调用的参数中就可以避免)-但是,虽然这本身并不难,但如果你以前没有做过类似的事情(例如,玩弄事物的内部结构,或其他稍微“低级”的东西),那可能是一座很难攀登的高山,具体取决于人;这可能不是一项快速的运动
那么,复杂度呢?首先,字节码是CPython的一个实现细节,所以这在诸如Jython之类的替代解释器中不起作用。此外,字节码可以从CPython版本更改到下一个版本,因此大多数CPython版本将需要稍微不同的解析代码
早些时候,我也说过“在某种程度上”——这是什么意思?我已经提到处理条件可能很难。此外,如果没有存储值,就无法获取变量名