1. 导入文件的方式获取文件中的类
由于工作需求,需要获得文件中的某一些类的内容。一般比较常用的方法是通过遍历文件,然后通过调用importlib包导入文件,就能获得文件中的内容,从而获取想要的类。代码如下:
for importer, package_name, is_pkg in pkgutil.walk_packages('.'):
importlib.import_module(package_name)
module = sys.modules[package_name]
for cls_name, cls in inspect.getmembers(module, inspect.isclass):
if is_want_class(cls):
do_something()
该方法十分简单,但是也有弊端。由于需要导入文件,因此文件中的内容都会被执行。而在一个项目中,存在着成百上千的文件,有一些代码是相斥的。如果通过这种方法获取类,轻则出现逻辑问题,重则出现异常,报错。
2.通过不导入文件的方式获取文件中的类名,从而过滤文件
为了防止上面说到的问题影响项目,我们不能将所有文件通通导入进来。但是换个思路,可以先通过不导入文件的方式获取文件中中存在的类名,如果存在的类名中有想要获取的类,再导入文件。通过这种方式避免导入所有文件,从而大大降低出错的可能性。
我们知道,python解释器在运行时候,对文件做了下列操作:
- Parse source code into a parse tree (Parser/pgen.c)
- Transform parse tree into an Abstract Syntax Tree (Python/ast.c)
- Transform AST into a Control Flow Graph (Python/compile.c)
- Emit bytecode based on the Control Flow Graph (Python/compile.c)
其中1,2步执行之后,会生成一棵抽象语法树,从而帮助解释器去解析代码结构。
因此想要获得文件中的类,可以通过ast包获得生成的ast树,再通过遍历树节点的方式获得相应的内容。通过该方法,可以获得定义的变量和类名的文本信息。
在项目代码中,一般对于类的命名有特定的规范。因此需要正则表达式,可以过滤出文件中是否含有想要查找的类。代码如下:
for root, dirs, files in os.walk("."):
for file in files:
file_name = os.path.join(root, file)
with open(file_name, "r") as source:
# 将文件转化为ast树
tree = ast.parse(source.read())
# 获取树中的类
class_name_list = [node.name for node in ast.walk(tree) if isinstance(node, ast.ClassDef)]
# 这里通过正则want_pattern去匹配类,从而过滤出想要的类
want_class_name_list = [name for name in class_name_list if re.search(want_pattern, name) is not None]
最后得到的want_class_name_list就是通过正则匹配出来的类的类名。通过判断want_class_name_list是否为空,就能知道文件中是否定义了自己想要的类。如果定义了,再用方法1获取文件中的类即可。