喵喔喔
这里有两种可能的方法。静态方法:您可以使用 ast 模块解析代码库以识别所有函数调用并一致地存储调用的来源和目标。您必须标识所有类和函数定义以跟踪每个调用的当前上下文。这里的限制是,如果您调用实例方法,则没有简单的方法来识别该方法实际属于哪个类。如果您使用引用模块的变量,则相同这是一个访问者子类,它可以读取 Python 源文件并构建一个 dict {caller: callee}:class CallMapper(ast.NodeVisitor): def __init__(self): self.ctx = [] self.funcs = [] self.calls = collections.defaultdict(set) def process(self, filename): self.ctx = [('M', os.path.basename(filename)[:-3])] tree = ast.parse(open(filename).read(), filename) self.visit(tree) self.ctx.pop() def visit_ClassDef(self, node): print('ClassDef', node.name, node.lineno, self.ctx) self.ctx.append(('C', node.name)) self.generic_visit(node) self.ctx.pop() def visit_FunctionDef(self, node): print('FunctionDef', node.name, node.lineno, self.ctx) self.ctx.append(('F', node.name)) self.funcs.append('.'.join([elt[1] for elt in self.ctx])) self.generic_visit(node) self.ctx.pop() def visit_Call(self, node): print('Call', vars(node.func), node.lineno, self.ctx) try: id = node.func.id except AttributeError: id = '*.' + node.func.attr self.calls['.'.join([elt[1] for elt in self.ctx])].add(id) self.generic_visit(node)动态方法:如果您真的想确定调用了什么方法,当多个方法可以共享相同的名称时,您将不得不使用动态方法。您可以装饰类中的单个函数或所有方法,以计算它们被调用的次数,以及它们的调用位置。然后您将开始测试并检查实际发生的情况。这是一个函数,它将装饰类中的所有方法,以便所有调用的数字都将存储在字典中:def tracemethods(cls, track): def tracker(func, track): def inner(*args, **kwargs): if func.__qualname__ in track: track[func.__qualname__] += 1 else: track[func.__qualname__] = 1 return func(*args, *kwargs) inner.__doc__ = func.__doc__ inner.__signature__ = inspect.signature(func) return inner for name, func in inspect.getmembers(cls, inspect.isfunction): setattr(cls, name, tracker(func, track))您可以调整该代码以浏览解释器堆栈以识别每个调用的调用者,但这不是很容易,因为您获得调用者函数的非限定名称,并且必须使用文件名和行号来唯一标识调用者.