如何获得函数调用的变量名

时间:2018-09-05 12:10:28

标签: python function variables names

我将要编写一个装饰器,该装饰器评估传递给函数调用的变量的实际名称(而不是其值)。

下面,您找到了代码的骨架,这使我想做什么更加清晰。

import functools

def check_func(func):
    # how to get variable names of function call
    # s.t. a call like func(arg1, arg2, arg3)
    # returns a dictionary {'a':'arg1', 'b':'arg2', 'c':'arg3'} ?
    pass

def my_decorator(func):
    @functools.wraps(func)
    def call_func(*args, **kwargs):
        check_func(func)
        return func(*args, **kwargs)

    return call_func

@my_decorator
def my_function(a, b, c):
    pass

arg1='foo'
arg2=1
arg3=[1,2,3]
my_function(arg1,arg2,arg3)

1 个答案:

答案 0 :(得分:1)

您真的无法满足您的要求。

有很多调用函数的方法,甚至不会获得单个值的变量名。例如,当您在调用中使用文字值时,名称将是什么,所以:

my_function('foo', 10 - 9, [1] + [2, 3])

或在使用带有值的列表进行*参数扩展时:

args = ['foo', 1, [1, 2, 3]]
my_function(*args)

或者当您使用functools.partial() object将某些参数值绑定到可调用对象时:

from functools import partial
func_partial = partial(my_function, arg1, arg2)
func_partial(arg3)

函数是通过对象(值)而不是变量传递的。仅由名称组成的表达式可能已用于生成对象,但是这些对象与变量无关。

Python对象可以具有许多不同的引用,所以仅仅因为该调用使用了arg1,并不意味着在其他地方不会有对该对象的其他引用。对您的代码很有趣。

您可以尝试分析调用该函数的代码(inspect module可以给您access to the call stack),但是前提是假定源代码可用。调用代码可能使用C扩展名,或者解释器只能访问.pyc字节代码文件,而不能访问原始源代码。您仍然必须追溯并分析调用表达式(并非总是那么简单,函数也是对象,可以存储在容器中,以后再检索以进行动态调用),然后从那里找到涉及的变量

对于普通情况,其中仅直接位置参数名称用于调用,并且整个调用被限制在一行源代码中,可以使用{{1}的组合}和ast module来将源解析为足以进行分析的内容:

inspect.stack()

再次强调:这仅适用于最基本的调用,其中调用仅由一行组成,并且被调用的名称与函数名称匹配。它可以扩展,但这需要大量工作。

对于您的特定示例,这将产生import inspect, ast class CallArgumentNameFinder(ast.NodeVisitor): def __init__(self, functionname): self.name = functionname self.params = [] self.kwargs = {} def visit_Call(self, node): if not isinstance(node.func, ast.Name): return # not a name(...) call if node.func.id != self.name: return # different name being called self.params = [n.id for n in node.args if isinstance(n, ast.Name)] self.kwargs = { kw.arg: kw.value.id for kw in node.keywords if isinstance(kw.value, ast.Name) } def check_func(func): caller = inspect.stack()[2] # caller of our caller try: tree = ast.parse(caller.code_context[0]) except SyntaxError: # not a complete Python statement return None visitor = CallArgumentNameFinder(func.__name__) visitor.visit(tree) return inspect.signature(func).bind_partial( *visitor.params, **visitor.kwargs) ,因此产生inspect.BoundArguments instance。使用<BoundArguments (a='arg1', b='arg2', c='arg3')>获取具有名称/值对的.arguments映射,或使用OrderedDict将其转换为常规字典。

您将不得不以不同的方式考虑您的特定问题。装饰器不是要对代码调用起作用,而是要对装饰的对象起作用。该语言还具有许多其他强大功能,可以帮助您处理调用上下文,而修饰符不是。