我将要编写一个装饰器,该装饰器评估传递给函数调用的变量的实际名称(而不是其值)。
下面,您找到了代码的骨架,这使我想做什么更加清晰。
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)
答案 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
将其转换为常规字典。
您将不得不以不同的方式考虑您的特定问题。装饰器不是要对代码调用起作用,而是要对装饰的对象起作用。该语言还具有许多其他强大功能,可以帮助您处理调用上下文,而修饰符不是。