Python在函数调用之外获取变量名

时间:2019-01-10 04:14:39

标签: 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()

的原始变量名

Alex Martelli's answer中一样,我们可以使用inspect模块来获取帧。但这在这种情况下无济于事,因为我不得不解析"f(arg1, arg2, arg3)\n",这很麻烦

还有其他选择吗?

2 个答案:

答案 0 :(得分:2)

您似乎正在尝试这种粗略的语法解决方法。改为传递dictkwargs

字典使用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

然后您可以使用字典的键查找参数名称。

答案 1 :(得分:1)

这可能不是可取的,但是可以通过解析调用者的字节码(至少在某种程度上)实现 。具体来说,您希望使用例如来获取调用者的堆栈框架。 frame = inspect.stack()[-2][0],然后在frame.f_code.co_code中查找原始字节码,并在frame.f_lasti中查找在该帧中执行的最后一条指令的索引-这将是引起功能的CALL_FUNCTION操作码被称为。 (只要您还没有回来-之后f_lasti就会随着调用者框架中执行的进行而更新)

现在,解析字节码一点也不困难,至少直到您重新构造控制流为止(如果可以假设被调用者不使用三元运算符{{ 1}}或and放在函数调用的参数中)-但是虽然这本身并不难,但是如果您之前没有做过类似的事情(例如,玩弄事物的内部结构,或其他少量的“低级”物品),这可能是要攀登的高山,具体取决于人员;这可能不是一个快速的练习。

那么,并发症呢?好吧,一方面,字节码是CPython的实现细节,所以这在其他解释器(例如Jython)中将不起作用。此外,字节码可以从CPython版本更改为下一版本,因此大多数CPython版本将需要稍有不同的解析代码。

更早些时候,我也说过“一定程度上”-我的意思是?好吧,我已经提到处理条件可能很难。此外,如果值未存储在变量中,则无法获取变量名称!例如,您的函数可能像orf(1, 2, 3)f(mylist[0], mydict['asd'], myfunc())那样被调用-在前两种情况下,您可能只是想决定要真正知道的是变量名而不是变量名对应于每个参数的表达式...但是最后一种情况呢?这两个表达式都可能对应多个参数,并且您不知道哪个参数来自哪个表达式!通常,您 都不知道-由于值来自函数调用,因此您不能在不再次调用函数的情况下查看它们-并且不能保证函数不会有任何副作用-效果,否则它们将再次返回相同的值。或者,也许像早期使用条件语句一样,您可以假定调用方没有执行任何操作。

简而言之,有可能-在某种程度上-甚至可能并不是很难做到-至少如果您知道自己在做什么-但这肯定不会变得简单。 >,快速常规建议


编辑:如果出于某种原因-毕竟-您仍然 出于某种原因想要这么做(或者您只是出于某种原因)好奇会是什么样子),这是一个极其有限的概念证明,可以帮助您入门。 (仅适用于CPython3.4,仅适用于位置参数,仅适用于局部变量。而且,对于<3.4,没有f(*make_args(), **make_kwargs()),因此解析必须手动完成。而且,随着您的使用,解析变得越来越复杂添加对更多操作码的支持-这可能取决于先前操作码的结果

get_instructions()