繁体   English   中英

从模块中获取类而无需实际运行文件

[英]Fetching classes from a module without actually having to run the file

继续我的这个问题,我现在的目标是解析一个 Python 文件,并

  1. 提取所有类
  2. 提取其属性列表和基类列表

加载文件(运行它)。

目前,我有这个工作代码:

解析器.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)

要解析的文件:

from c import CClass
    
class MyClass(UndefinedClass):
    name = 'Edgar'

    def foo(self, x):
        print(x)


def func():
    print('Hello')

这个解决方案的好处是,即使给定文件 a.py 包含无效的 python 代码,我也能得到类名列表。 看起来我必须更深入地研究 AST 模块。 有什么办法可以提取类属性及其基类的列表吗?

您可以使用递归遍历ast.parse生成的ast 下面的解决方案不仅在主输入文件中执行此搜索,而且还在任何后续导入的文件中执行此搜索:

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)

将它们与您的两个原始文件放在一起:

文件 c.py:

c_var = 2

class CClass:
   name = 'Anna'

文件 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)

输出:

{'CClass': {'bases': [], 'attrs': ['name']}, 'MyClass': {'bases': ['UndefinedClass'], 'attrs': ['name']}}

ClassDef节点有一个bases属性,它是一个节点列表,表示该类的基类。 它还有一个body属性,它是一个代表类定义主体的节点列表。 我想你想要正文中的Assign节点,但也许你的意思是类属性略有不同。

https://docs.python.org/3/library/ast.html#ast.ClassDef

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM