Python之模块导入(不看会后悔系列)

看到这个标题猜想大家内心OS: 什么辣鸡水文,划走划走~



别急有干货!

  • 静态导入(照顾新人)
    假设现在有两个文件a,b在不同目录,b文件想引用a文件中的函数:
# test_module/sub_module_a/a.pydef a(): print(f"i`m function a")################################################# # test_module/sub_module_b/b.pydef b(): print("i`m function b")

三种比较常规的方法:
  1. from xx_module import xx_function
# test_module/sub_module_b/b.py from sub_module_a.a import a# 从sub_module_a/a.py 导入function adef b(): a()# 导入后就可以直接用function a了 print("i`m function b")

  1. 临时添加模块的绝对路径:
    可以临时将所需模块路径添加到 sys.path 变量中
# test_module/sub_module/b.py import sys # 向sys.path中追加a模块所在的绝对路径 sys.path.append('/Users/mac/Desktop/test_module/sub_module_a')from a import a# 从a.py 导入 function adef b(): a() print("i`m function b")if __name__ == '__main__': b() # 输出: # i`m function a # i`m function b

  1. 将模块保存到扩展包下, 如: lib\site-packages
    Python解释器在执行代码遇到import时查找模块名是在sys.path中查找, 如果找不到则抛异常ModuleNotFoundError
# test_module/sub_module_b/b.py import sysprint(sys.path) import adef b(): a.a() print("i`m function b")if __name__ == '__main__': b()

Python之模块导入(不看会后悔系列)
文章图片

可以将所需的模块放入上图的任意一个路径下再使用import语句则可正常导入
比如我们可以将上文的a.py放到父级目录也就是test_module下再在b.py中进行引用
Python之模块导入(不看会后悔系列)
文章图片

或者放到lib/site-packages(默认扩展包路径)下面, 如:Python之模块导入(不看会后悔系列)
文章图片

  • ??动态导入
    假设现有文件结构为:
├── debugs │├── __init__.py │├── a.py │└── b.py └── main.py

文件a和b中假设有相同的函数test(),但内部实现逻辑不一致; 我们在main模块中根据用户传入的参数来决定调用a模块或者b模块的test()函数
a.py
# test_module/debug/a.pydef test(): print(f"i`m in a.py")

b.py
# test_module/debug/b.pydef test(): print(f"i`m in b.py")

常规操作main的写法:
def main(arg): if arg == 'a': from debug.a import test elif arg == 'b': from debug.a import test test()main('b')# i`m in b.py

假设用动态导入可以写成:
  1. exec方法
def main(arg): exec(f'from debug.{arg} import test')# 利用exec可执行字符串的逻辑导入模块 locals().get('test')()#locals()和globals()保存了当前的所有变量if __name__ == '__main__': main('b')# i`m in b.py

  1. importlib模块的import_module方法
import importlibdef main(arg): module = importlib.import_module(f'debug.{arg}')# 绝对导入 module.test() module2 = importlib.import_module(f'.{arg}', package='debug')# 相对导入 module2.test() getattr(module, 'test')()# 也可以利用反射机制,这个很重要!!!是动态导入的绝佳搭配if __name__ == '__main__': main('b')# 会输出3遍 i`m in b.py

  • ??案例!
    博主开发了一个web的丐版postman,其中支持pre request script脚本隔离; 模板变量参数化时就需要用到动态导入+反射
    整个项目部分结构为:
│── debugtalks # 配置文件 │├── __init__.py │├── cur_bc_id_1.py │└── cur_bc_id_2.py └── view # 视图函数 │├── __init__.py │└── PreReqeustHandle.py# 前置处理模板变量

其中部分测试内容为:
cur_bc_id_1.py
# cur_bc_id_1.py # dependence:需要引用的第三方依赖模块 dependence = [] variable=1def func(arg1,arg2): return arg1+arg2

PreReqeustHandle.py部分源码为
def request_temp(module, data): if not isinstance(data, (dict, list, tuple)): pass elif isinstance(data, (list, tuple)): for index in data: request_temp(module, index) elif isinstance(data, dict): for k, v in data.items(): if isinstance(v, str): regex_result = re.findall(r"^.*\$\{([^}]+)}.*$", str(v)) if regex_result: try: if not str(regex_result[0]).endswith(')'): data[k] = getattr(module, regex_result[0]) else: params = re.findall(r"^.*\(([^}]+)\).*$", str(regex_result[0])) func_name = str(regex_result[0]).split('(')[0] if not params: data[k] = getattr(module, func_name)() else: variables = str(params[0]).split(',') param = {data.split("=")[0]: eval(data.split("=")[1]) for data in variables} data[k] = getattr(module, func_name)(**param)except Exception as e: data[k] = f"{regex_result[0]} 参数化引用失败,{e} 请检查是否有误!" else: request_temp(module, v) return data

【Python之模块导入(不看会后悔系列)】博主是用flask开发的项目所以在before_request中对每次请求进来的根据参数cur_bc_id=xx,再去相应debugtalks下面利用动态导入+PreReqeustHandle里的反射将模板变量(规则为:"${function(arg=value)}"或"${variable}")替换成cur_bc_id_xx.py的引用对象,然后再请求具体接口
Python之模块导入(不看会后悔系列)
文章图片

由用户在线编辑python代码保存后,请求体内即可引用; 也可在线调试自己的代码下方展示区显示运行结果
Python之模块导入(不看会后悔系列)
文章图片

    推荐阅读