[英]Fetching classes from a module without actually having to run the file
Going on this question of mine , my goal now is to parse a Python file, and to继续我的这个问题,我现在的目标是解析一个 Python 文件,并
Without loading the file (running it).不加载文件(运行它)。
Currently, I have this working code:目前,我有这个工作代码:
parser.py解析器.py
import ast
def get_classes(path):
with open(path) as fh:
root = ast.parse(fh.read(), path)
classes = []
for node in ast.iter_child_nodes(root):
if isinstance(node, ast.ClassDef):
classes.append(node.name)
else:
continue
return classes
for c in get_classes('a.py'):
print(c)
File to be parsed:要解析的文件:
from c import CClass
class MyClass(UndefinedClass):
name = 'Edgar'
def foo(self, x):
print(x)
def func():
print('Hello')
The good part of this solution is that I get the list of class names even given that file a.py contains invalid python code.这个解决方案的好处是,即使给定文件 a.py 包含无效的 python 代码,我也能得到类名列表。 Looks like I have to dig deeper into AST module.
看起来我必须更深入地研究 AST 模块。 Is there any way I can extract the list of class attributes and its base classes ?
有什么办法可以提取类属性及其基类的列表吗?
You can use recursion to traverse the ast
produced by ast.parse
.您可以使用递归遍历
ast.parse
生成的ast
。 The solution below performs this search not only in the main input file but in any subsequent imported files as well:下面的解决方案不仅在主输入文件中执行此搜索,而且还在任何后续导入的文件中执行此搜索:
import ast, importlib
class Parse:
def __init__(self):
self.c = {}
def walk(self, tree, f = None):
if isinstance(tree, ast.ClassDef):
self.c[tree.name] = {'bases':[i.id for i in tree.bases], 'attrs':[]}
for i in tree.body:
self.walk(i, tree.name)
elif isinstance(tree, (ast.ImportFrom, ast.Import)):
for i in (k if isinstance((k:=getattr(tree, 'module', tree.names)), list) else [k]):
with open(importlib.machinery.PathFinder().find_module(getattr(i, 'name', i)).get_filename()) as f:
self.walk(ast.parse(f.read()))
elif isinstance(tree, ast.Assign) and f is not None:
self.c[f]['attrs'].append(tree.targets[0].id)
else:
for i in getattr(tree, '_fields', []):
for j in (k if isinstance((k:=getattr(tree, i)), list) else [k]):
self.walk(j, None)
Putting it all together with your two original files:将它们与您的两个原始文件放在一起:
File c.py:文件 c.py:
c_var = 2
class CClass:
name = 'Anna'
File a.py:文件 a.py:
from c import CClass
class MyClass(UndefinedClass):
name = 'Edgar'
def foo(self, x):
print(x)
def func():
print('Hello')
p = Parse()
with open('a_mod_test.py') as f:
p.walk(ast.parse(f.read()))
print(p.c)
Output:输出:
{'CClass': {'bases': [], 'attrs': ['name']}, 'MyClass': {'bases': ['UndefinedClass'], 'attrs': ['name']}}
A ClassDef
node has a bases
attribute which is a list of nodes representing the base classes for the class. ClassDef
节点有一个bases
属性,它是一个节点列表,表示该类的基类。 It also has a body
attribute which is a list of nodes representing the body of the class definition.它还有一个
body
属性,它是一个代表类定义主体的节点列表。 I suppose you want the Assign
nodes in the body, but maybe you mean something slightly different by class attributes.我想你想要正文中的
Assign
节点,但也许你的意思是类属性略有不同。
https://docs.python.org/3/library/ast.html#ast.ClassDef https://docs.python.org/3/library/ast.html#ast.ClassDef
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.