我想使用内省(introspection)从函数本身存储为函数调用键入的确切字符串(我不能/不想破解命令行解释器——因此,例如从 readline 或任何不是我要找的东西)。
假设,如果用户输入:
>>> myfunc('a', 'b', 1, mykwarg='hello')
我想获取调用字符串(即 myfunc('a', 'b', 1, mykwarg='hello')
)
来自 myfunc
中的代码。
我可以制作这样的东西:
def myfunc(a,b,c,mykwarg=None):
frame = inspect.currentframe()
sig = inspect.signature(myfunc)
args = []
for param in sig.parameters.values():
if param.name in frame.f_locals:
args.append(f"{param.name}={str(frame.f_locals[param.name])}")
cmd = f"{frame.f_code.co_name}({','.join(args)})"
print(cmd)
我得到:
>>> myfunc('a', 'b', 1, mykwarg='hello')
myfunc(a=a,b=b,c=1,mykwarg=hello)
这不完全是用户输入的内容。另外,我希望有更多的东西 健壮且不那么“老套”的尝试...
用例:我希望能够将来 self 的库的命令调用与其结果相关联。我不想为每个函数硬编码命令调用存储,我更愿意使用装饰器或类似的东西。这可能在 REPL 中更容易做到,但我不想依赖它(比如,如果用户从自己的程序调用该函数,它仍然应该能够将命令调用与结果相关联)。
最佳答案
最后我回答了我自己的问题,希望有一天它能对其他人有所帮助。
我决定尝试采用 dis
方式,即。 “反汇编”的Python代码对象
调用我的函数的外部框架,看看它是如何被调用的
重构命令行:
import dis
import inspect
import io
import ast
import re
def get_function_call_string():
in_func = False
args=[]
kwargs=[]
func_name = inspect.stack()[1][3]
frame = inspect.currentframe().f_back.f_back
dis_output = io.StringIO()
dis.dis(frame.f_code, file=dis_output)
for line in dis_output.getvalue().split('\n'):
instr = re.findall(r'^.*\s([A-Z_]+)\s*', line)[0]
if instr.startswith("CALL_FUNCTION") and in_func:
break
elif instr.startswith('LOAD_'):
name = re.findall(r'^.*\s\((.+)\)$', line)[0]
if in_func:
if name.startswith('('):
kwargs = ast.literal_eval(name)
else:
args.append(name)
elif name == func_name:
in_func = True
kwargs = [f"{a}={args.pop()}" for a in kwargs]
return f"{func_name}({', '.join(args)}{', ' if kwargs else ''}{', '.join(kwargs)})"
例子:
>>> def f(a,b,arg="toto"):
print(get_function_call_string())
>>> f(1,"test","example")
f(1, 'test', 'example')
>>> f(1, "test", arg="hello")
(1, 'test', arg='hello')
这不是一个完整的答案,因为它无法处理某些参数类型,例如 dict 或 list...我可能会继续这样做,或者改变主意,做不同的事情。
关于python - 获取 Python 函数的调用字符串,如在命令行中键入的那样,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56003055/