解析Python函数调用以获取参数位置
我需要能够分析函数调用的代码,如下所示:解析Python函数调用以获取参数位置,python,syntax,lexical-analysis,Python,Syntax,Lexical Analysis,我需要能够分析函数调用的代码,如下所示: whatever(foo, baz(), 'puppet', 24+2, meow=3, *meowargs, **meowargs) 并返回每个参数的位置,在本例中为foo,baz(),'puppet',24+2,meow=3,*meowargs,**meowargs 我试着使用\u ast模块,它似乎正适合这项工作,但不幸的是出现了问题。例如,在像baz()这样的参数中,它本身就是一个函数调用,我找不到一种简单的方法来获取它的长度。(即使我找到了一
whatever(foo, baz(), 'puppet', 24+2, meow=3, *meowargs, **meowargs)
并返回每个参数的位置,在本例中为
foo
,baz()
,'puppet'
,24+2
,meow=3
,*meowargs
,**meowargs
我试着使用\u ast
模块,它似乎正适合这项工作,但不幸的是出现了问题。例如,在像baz()
这样的参数中,它本身就是一个函数调用,我找不到一种简单的方法来获取它的长度。(即使我找到了一个,我也不希望每种不同的论点都有一大堆特殊的案例。)
我还查看了tokenize
模块,但看不到如何使用它来获取参数
您知道如何解决这个问题吗?您可能需要获取函数调用的抽象语法树 ,基于
ast
模块
Python的ast模块用于解析代码字符串并创建ast
节点。然后,它遍历生成的ast.ast节点以查找
使用NodeVisitor子类的功能
函数explain
执行解析。下面是您分析函数调用以及得到的结果
>>> explain('mymod.nestmod.func("arg1", "arg2", kw1="kword1", kw2="kword2",
*args, **kws')
[Call( args=['arg1', 'arg2'],keywords={'kw1': 'kword1', 'kw2': 'kword2'},
starargs='args', func='mymod.nestmod.func', kwargs='kws')]
如果我理解正确,从您的示例中,您希望类似于:
--> arguments("whatever(foo, baz(), 'puppet', 24+2, meow=3, *meowargs, **meowkwds)")
{
'foo': slice(9, 12),
'baz()': slice(14, 19),
'24+2': slice(21, 29),
'meow=3': slice(32, 38),
'*meowargs': slice(41, 50),
'**meowkwds': slice(53, 63),
}
请注意,我更改了上一个参数的名称,因为不能有两个同名的参数
如果这是您想要的,那么您需要有问题的原始字符串(如果您构建IDE,这应该不是问题),并且您需要一个字符串解析器。一个简单的状态机应该可以做到这一点。此代码使用
ast
(查找初始参数偏移量)和正则表达式(标识参数边界)的组合:
输出:
whatever(foo, baz(), 'puppet', 24+2, meow=3, *meowargs, **meowargs)
^ ^
^ ^
^ ^
^ ^
^ ^
^ ^
^ ^
[(9, 12), (14, 19), (21, 29), (31, 35), (37, 43), (45, 54), (56, 66)]
f(1, len(document_text) - 1 - position)
^
^ ^
[(2, 3), (5, 38)]
“并返回每个参数的位置,在本例中,
foo
,baz()
,'puppet'
,24+2
,meow=3
,*meowargs
,**meowargs
”您想返回什么?你怎么知道你的电话是什么?有什么用?你想做什么还不清楚,我也不确定你想做什么,但我很确定正确的、最好的、最健壮的方法是查看AST(最好通过AST
模块,\u AST
是一个实现细节,AST
添加了一些有用的功能)。您需要了解ASTs和树遍历的概念,但如果不了解这一点,您肯定会产生一个缓慢、复杂、有限、脆弱的解决方案,不管怎样,它会影响位置,即它们在字符串中开始和结束的索引。使用的是IDE脚本。我想不出你关于这个电话的问题。还是不清楚你想要什么。您想要调用什么(即函数内部)还是可以调用什么(例如IDE尝试排列正确的参数)。请参见foo
?我想要一个元组,其中第一项是f
的位置,第二项是最后一个o
的位置。我看不出这有什么帮助。例如,如果你的一个参数是函数调用本身,你如何知道它的开始位置和结束位置?我很感激你能帮助我,但是当你说“一个简单的状态机应该可以做到这一点”时,我不知道你的意思是什么,以及如何构建它,这样它才能真正工作并返回我想要的结果。(是的,我有这个字符串。)(我在高中时确实学习过状态机,但从它到工作解决方案的方法对我来说并不清楚。)@RamRachum:它们不太难——尽管如此,我从未真正实现过。我会看看我是否能抽出时间聚在一起。@EthanFurman:是的。这并不太难,但由于必须解析Python语法的相当一部分,因此可能会变得单调乏味。我会使用pyparsing
或ply
来代替。令人印象深刻的黑客。我希望有可能创建一个不使用regex的解决方案(因为它对于此类任务来说通常是一个不好的工具),但我承认这可能是不可能的。但是,您的解决方案在“Foo(x=y,\n**kwargs)”
中失败。如果结尾数字再高一个可能更好——那么它可以直接用作字符串片段,或范围
参数。对于“f(1,len(文档文本)-1-位置)”失败
。这将成为一个真正的黑客。它现在适用于f(1,len(文档文本)-1-位置)
。我还考虑了@EthanFurman的评论。
whatever(foo, baz(), 'puppet', 24+2, meow=3, *meowargs, **meowargs)
^ ^
^ ^
^ ^
^ ^
^ ^
^ ^
^ ^
[(9, 12), (14, 19), (21, 29), (31, 35), (37, 43), (45, 54), (56, 66)]
f(1, len(document_text) - 1 - position)
^
^ ^
[(2, 3), (5, 38)]