动态调用python类和函数

遇到一个需求,需要尽可能的尝试触发python模块里的行为,比如函数,类实例这样,感觉和java里的反射有点像;通过调研发现python里有getattr这个方法,类似于java里的反射机制,可以通过字符串比较方便的获取到类里的成员函数。通过importlib导入模块,然后通过inspect检查函数和类,最后在通过getattr获取对应对象执行。pytimer和packadds是我自写的不影响整体功能的函数,防止超时和解压文件。

"""
PyWalker:根据路径动态导入py文件,并尝试创建类实例和执行函数
"""
import sys
import importlib
import inspect
import pytimer
import packadds



class PyWalker:
    """
    动态的导入python模块并尝试触发其中的不同方法
    """
    timer = pytimer.PyTimer()
    packer = packadds.PackProcessor()

    def __init__(self):
        """
        初始化
        """

    @staticmethod
    def read_module(module_path: str):
        """
        读取py文件的路径,尝试将其导入
        importlib的路径比较特殊,如test/sample.py需要转化变为
        test.sample
        """
        if '/' in module_path:
            module_path = module_path.replace('/', '.')
        if '\\' in module_path:
            module_path = module_path.replace('\\', '.')
        if module_path.endswith('.py'):
            module_path = module_path[:-3]
        module_imported = importlib.import_module(module_path)
        return module_imported

    @staticmethod
    def get_function_name(module_imported):
        """
        获取模组里的所有函数名
        """
        function_name_list = []
        for member_name, _ in inspect.getmembers(module_imported, inspect.isfunction):
            function_name_list.append(member_name)
        print("函数", function_name_list)
        return function_name_list

    @staticmethod
    def get_class_name(module_imported):
        """
        获取模组里的所有类名
        """
        class_name_list = []
        for member_name, _ in inspect.getmembers(module_imported, inspect.isclass):
            class_name_list.append(member_name)
        print("类", class_name_list)
        return class_name_list

    @staticmethod
    def eval_function(module_imported, function_name: str, *args):
        """
        根据函数名动态调用函数
        """
        module_function = getattr(module_imported, function_name)
        function_return = module_function(*args)
        return function_return

    @staticmethod
    def parse_module(module_path: str):
        """
        解析模组,获取函数名和类名,然后尝试执行函数
        """
        print("导入", module_path, "输出:")
        module_imported = PyWalker.read_module(module_path)
        class_name_list = PyWalker.get_class_name(module_imported)
        function_name_list = PyWalker.get_function_name(module_imported)
        for each_class in class_name_list:
            PyWalker.timer.count_time(PyWalker.parse_class, module_imported, each_class)
        for each_function in function_name_list:
            PyWalker.timer.count_time(PyWalker.eval_function, module_imported, each_function)

    @staticmethod
    def parse_class(module_imported, each_class):
        """
        解析类,创建实例,尝试执行函数
        """
        module_class = getattr(module_imported, each_class)
        function_name_list = PyWalker.get_function_name(module_class)
        if function_name_list[0] == '__init__':
            # 因为已经创建过实例所以不再执行init
            function_name_list.pop(0)
        class_instance = module_class()
        for each_function in function_name_list:
            PyWalker.eval_function(class_instance, each_function)
        if PyWalker.timer.get_errors():
            print(PyWalker.timer.get_errors())

    @staticmethod
    def parse_packet(packet_path: str):
        """
        输入一个whl或zip包的路径,尝试解压然后逐个导入运行
        """
        if packet_path.endswith('.py'):
            # 如果是py文件就不用解压了
            pyfile_list = [packet_path]
        else:
            unzipped_dir = PyWalker.packer.unzip_path(packet_path)
            pyfile_list = PyWalker.packer.get_dir_files(unzipped_dir, '.py')
        for each_pyfile in pyfile_list:
            try:
                PyWalker.parse_module(each_pyfile)
            except Exception as e:
                print("error:", each_pyfile, e)
        PyWalker.packer.delete_unzips()


if __name__ == '__main__':
    file_path = sys.argv[1]
    my_walker = PyWalker()
    my_walker.parse_packet(file_path)

你可能感兴趣的:(python,windows,开发语言)