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

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



别急有干货!

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

def a():
   print(f"i`m function a")
   
#################################################
# test_module/sub_module_b/b.py

def 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 a

def 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 a

def 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 sys

print(sys.path)
import a

def b():
	a.a()
	print("i`m function b")


if __name__ == '__main__':
	b()

在这里插入图片描述
可以将所需的模块放入上图的任意一个路径下再使用import语句则可正常导入
比如我们可以将上文的a.py放到父级目录也就是test_module下再在b.py中进行引用
Python之模块导入(不看会后悔系列)_第1张图片
或者放到lib/site-packages(默认扩展包路径)下面, 如:Python之模块导入(不看会后悔系列)_第2张图片

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

文件a和b中假设有相同的函数test(),但内部实现逻辑不一致; 我们在main模块中根据用户传入的参数来决定调用a模块或者b模块的test()函数
a.py

# test_module/debug/a.py


def test():
	print(f"i`m in a.py")

b.py

# test_module/debug/b.py


def 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 importlib


def 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=1

def 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

博主是用flask开发的项目所以在before_request中对每次请求进来的根据参数cur_bc_id=xx,再去相应debugtalks下面利用动态导入+PreReqeustHandle里的反射将模板变量(规则为:"${function(arg=value)}"或"${variable}")替换成cur_bc_id_xx.py的引用对象,然后再请求具体接口
Python之模块导入(不看会后悔系列)_第3张图片
由用户在线编辑python代码保存后,请求体内即可引用;也可在线调试自己的代码下方展示区显示运行结果
Python之模块导入(不看会后悔系列)_第4张图片

你可能感兴趣的:(Python之模块导入(不看会后悔系列))