从方法对象中获取方法调用及其参数

tho*_*dic 5 python abstract-syntax-tree inspect python-2.7

使用 pythonsinspect模块,我已经隔离了一个方法对象,我现在需要遍历方法中的源代码以查找对某些其他方法的调用并获取它们的参数。

例如,假设在以下类中:

def my_method():
    print('hello')
    foobar('apple', 'pear', 6)
    print('world')
    foobar(1, 2, 3)
    return foobar('a', 'b')
Run Code Online (Sandbox Code Playgroud)

我需要提取传递给的参数列表foobar()

[('apple', 'pear', 6), (1, 2, 3), ('a', 'b', None)]
Run Code Online (Sandbox Code Playgroud)

可以假设所有参数都是硬编码的而不是动态的。

给定inspect包中的方法对象,我如何检查所述方法中的方法调用?

尝试

  • 我试过使用正则表达式,inspect.getsourcelines(method)但是如果参数语法发生变化,这会中断。
  • 我已经研究了带有 pythonsast模块的抽象语法树,但还没有找到任何解决方案。
  • 必须有一种方法来完成这个使用,inspect但我再次没有找到任何解决方案。

Pad*_*ham 5

这并不完美,但应该是一个开始,我稍后会添加一个更好的实现:

from ast import parse, Call, walk
import importlib
import inspect

mod = "test"
mod = importlib.import_module(mod)
p = parse(inspect.getsource(mod))

from ast import literal_eval

vals = []
for node in p.body:
    if isinstance(node, FunctionDef) and node.name == "my_method":
        for node in walk(node):
            if isinstance(node,Call) and node.func.id == "foobar":
                vals.append([literal_eval(val) for val in node.args])

print(vals)

[['apple', 'pear', 6], [1, 2, 3], ['a', 'b']]
Run Code Online (Sandbox Code Playgroud)

test.py 看起来像:

def foobar(a=0, b=0, c=None):
    return a, b, c

def other_method(x,y,z):
    return  x,y,z

def my_method():
    print('hello')
    foobar('apple', 'pear', 6)
    print('world')
    foobar(1, 2, 3)
    for i in range(10):
        if i > 9:
            s = foobar(4, 5, 6)
            print(s)
    return foobar('a', 'b')


def my_method2():
    foobar('orange', 'tomatoe', 6)
    foobar(10, 20, 30)
    for i in range(10):
        if i > 9:
            foobar(40, 50, 60)
    other_method("foo","bar","foobar")
    return foobar('c', 'd')
Run Code Online (Sandbox Code Playgroud)

如果您混合使用两者,则需要结合以某种方式将调用更改print('world')foobar(a=1, b=2, c=3)

vals = []
for node in p.body:
    if isinstance(node, FunctionDef) and node.name == "my_method":
        for node in walk(node):
            if isinstance(node, Call) and node.func.id == "foobar":
                kws = node.keywords
                if kws:
                    print("Found keywords",[(kw.arg, literal_eval(kw.value)) for kw in kws])
                else:
                    print([literal_eval(val) for val in node.args])
Run Code Online (Sandbox Code Playgroud)

输出:

[['apple', 'pear', 6], [], ['a', 'b'], [4, 5, 6]]
['apple', 'pear', 6]
('Found keywords', [('a', 1), ('b', 2), ('c', 3)])
['a', 'b']
[4, 5, 6]
Run Code Online (Sandbox Code Playgroud)

使用 ast.Nodevisitor 查找所有 Call 对象将返回所有函数中对“foobar”的所有调用:

from ast import parse, NodeVisitor, literal_eval
import importlib
import inspect

mod = "test"
mod = importlib.import_module(mod)
p = parse(inspect.getsource(mod))


class FindCall(NodeVisitor):
    def __init__(self, *args):
        if len(args) < 1:
            raise ValueError("Must supply at least ine target function")
        self.result = {arg: []for arg in args}

    def visit_Call(self, node):
        if node.func.id in self.result:
            self.result[node.func.id].append(map(literal_eval, node.args))
        # visit the children
        self.generic_visit(node)



fc = FindCall("foobar")
fc.visit(p)
print(fc.result)
Run Code Online (Sandbox Code Playgroud)

输出:

from pprint import pprint as pp
pp(fc.result)

{'foobar': [['apple', 'pear', 6],
            [1, 2, 3],
            [4, 5, 6],
            ['a', 'b'],
            ['orange', 'tomatoe', 6],
            [10, 20, 30],
            [40, 50, 60],
            ['c', 'd']],
 'other_method': [['foo', 'bar', 'foobar']]}
Run Code Online (Sandbox Code Playgroud)

您可以再次添加对 kwargs 的搜索,并且仅搜索特定功能。