这篇文章主要介绍“python模块引入问题如何解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“python模块引入问题如何解决”文章能帮助大家解决问题。
1.pycharm运行python脚本的过程
使用pycharm等编辑器run/debug运行python脚本时,编辑器会通过本地python命令全路径执行脚本,例如
D:DevelopToolsPythonpython.exe D:/Codes/一长串路径/bbss_nature_python/demo/test_no_param_in.py
并且会在python系统环境命令路径prepend即append first拼接启动脚本的绝对目录路径和脚本依赖的绝对目录路径,例如
print ("sys.path") ['D:Codes一长串路径\bbss_nature_pythondemo', 'D:Codes一长串路径\bbss_nature_python', ...]
这是由于pycharm编辑器run/debug配置中自动定义了全路径工作目录、全路径启动脚本、环境命令拼接等,如下图
2.python命令运行python脚本常见模块引入问题
例1
D:Codescust ycustbss_nature_cust_2021-2030bss_nature_cust>python bbss_nature_python/demo/test_no_param_in.py
在以上工作目录,使用相对路径运行python脚本,报错如下(绝对路径也是同样的报错)
Traceback (most recent call last):
File "D:Codescust
ycustbss_nature_cust_2021-2030bss_nature_custbss_nature_pythondemo est_no_param_in.py", line 17, in <module>
from common_code import ScriptReturnClz, _object2json_str
ModuleNotFoundError: No module named 'common_code'
例2
D:Codescust ycustbss_nature_cust_2021-2030bss_nature_cust>python bbss_nature_python/demo/test_no_param_in.py
在以上工作目录,使用相对路径运行python脚本,报错如下(绝对路径也是同样的报错)
Traceback (most recent call last):
File "D:Codescust
ycustbss_nature_cust_2021-2030bss_nature_custbss_nature_pythondemo est_no_param_in.py", line 18, in <module>
from ..common_code import ScriptReturnClz, _object2json_str
ImportError: attempted relative import with no known parent package
3.python命令运行python脚本模块引入问题的解决方案
Python在引用库文件时,会自动从python安装目录的Lib子目录等目录查找依赖,默认查找路径可以通过命令print(sys.path)查看
print ("sys.path") ['D:Codescust ycustbss_nature_cust_2021-2030bss_nature_custbss_nature_pythondemo', 'D:DevelopToolsPythonpython310.zip', 'D:DevelopToolsPythonDLLs', 'D:DevelopToolsPythonlib', 'D:DevelopToolsPyt hon', 'D:DevelopToolsPythonlibsite-packages']
可以看到,python命令执行一个python脚本前,会在sys.path数组第一位拼接启动脚本的目录绝对路径
测试用例目录文件树形图如下
3.1模块和当前脚本在同一目录下
在引用自定义模块时,如果模块和当前脚本在同一目录下,引入的时候,有以下几种方式:
1.直接模块名引入√【不建议】
如
from test_with_param_in import _print_json_param
此时,pycharm编辑器会告警,但python、pycharm运行都是正常的
可以选择忽略告警(告警右键选择ignore)
2.包名.模块名引入√【推荐,参见后文最佳方案】
如
from demo.test_with_param_in import _print_json_param
此时,pycharm编辑器不会告警,但python运行会报错
Traceback (most recent call last):
File "D:Codescust ycustbss_nature_cust_2021-2030bss_nature_custbss_nature_pythondemo est_no_param_in.py", line 16, in <module>
from demo.test_with_param_in import _print_json_param
ModuleNotFoundError: No module named 'demo'
此时需要引入缺少的模块所在的目录的绝对路径,如下
current_script_dir = os.path.split(os.path.realpath(__file__))[0] current_script_dir_parent = os.path.split(current_script_dir)[0] sys.path.insert(0, current_script_dir_parent)
3..模块名引入×【不可行】
如
from .test_with_param_in import _print_json_param
此时,pycharm编辑器不会告警,但python运行会报错
Traceback (most recent call last):
File "D:Codescust ycustbss_nature_cust_2021-2030bss_nature_custbss_nature_pythondemo est_no_param_in.py", line 16, in <module>
from .test_with_param_in import _print_json_param
ImportError: attempted relative import with no known parent package
无解决方案
3.2模块和当前脚本在不同目录下
如果模块和当前脚本不在同一目录下,必须指定模块路径,可以在当前脚本中import sys然后
通过sys.path.append(‘待引入的模块路径’)或者sys.path.insert(0,‘待引入的模块路径’)拼接的方式加入python系统命令路径,建议insert
待引入的模块路径,应该使用绝对(abs)路径,对于相对路径,python会直接拼接工作目录+相对路径生成绝对路径,拼接后的绝对路径是错误的(除非工作目录本身就是启动脚本的目录)
脚本内容:
# 当前脚本abs目录 current_script_dir = os.path.split(os.path.realpath(__file__))[0] # 当前脚本目录的上级abs目录(对于本测试用例即为待引入模块的abs目录) current_script_dir_parent = os.path.split(current_script_dir)[0] # 待引入模块common_code的abs目录加入系统环境命令路径数组 sys.path.insert(0, current_script_dir_parent) print("print ("sys.path")") print(sys.path) # 引入自定义模块common_code【注意,代码中必须先添加路径之后才能引入】 from common_code import ScriptReturnClz, _object2json_str
命令:
D:Codescust ycustbss_nature_cust_2021-2030bss_nature_cust>python bbss_nature_python/demo/test_no_param_in.py
运行结果:
print ("sys.path")
['D:Codescust
ycustbss_nature_cust_2021-2030bss_nature_custbss_nature_python', 'D:Codescust
ycustbss_nature_cust_2021-2030bss_nature_custbss_nature_pythondemo', 'D:DevelopToolsPythonpython310.zi
p', 'D:DevelopToolsPythonDLLs', 'D:DevelopToolsPythonlib', 'D:DevelopToolsPython', 'D:DevelopToolsPythonlibsite-packages']
4.python命令运行python脚本模块引入的最佳方案
4.1复杂工程的依赖遍历问题
当待引入的模块和当前脚本在不同目录下时,需要在环境变量中加入待引入模块目录的绝对路径,每个类似的启动脚本(即python命令执行的脚本)都需要编写很多类似的模板代码,而且由于依赖的传递性,可能无法简单地遍历出所有的自定义依赖模块
4.2 IDE的依赖默认引入方式
复杂依赖的python脚本,尤其是系统级规模化的python工程,通常使用pycharm等IDE编写。IDE会创建python工程,并且以工程目录为工作目录,在编写py脚本时,从工作目录开始查找自定义模块,自动补齐工作目录下的相对路径。另外,在打包python工程时,也是从工程目录开始。因此,使用IDE的默认的补全模块路径的依赖引入方式是最优选择
4.3多工程依赖的解决方案
不同于java微服务子工程之间的依赖引用,python没有子工程概念,只有一个完整的未拆分的工程。如果两个工程之间有模块依赖关系,则需要同时部署两个工程,并使用绝对路径引入另一个工程的工程目录,然后从工程目录开始引入所需模块。对于常用的自定义的基础依赖,也可以打包发布到公有源或私有源,直接install引入模块。从工程角度考虑,需要区分模块的命名空间,应采用和java包名类似的方式,以组织或个人的标识创建模块总包
4.4找到python工程的工程目录【方案本案】
鉴于IDE的依赖引入方式,只要将python工程的工程目录加入sys.path,则工程内的任何一个pycharm可成功运行的启动脚本都可以用python命令成功运行
怎样可以方便地找到一个python工程的工程目录?这取决于一个工程有多少启动脚本,以及每个启动脚本的位置
如果一个python工程只有一个启动脚本,比如一个python文件服务器,那么这个脚本应该直接放在工程一级目录下(如下图中工程目录为D: mppython,start.py为启动脚本),这样可以顺利找到当前工程中的所有自定义依赖,这是因为启动脚本路径自动拼接环境变量
如果一个python工程有多个启动脚本,并且不是都在工程目录下,比如一个python计算工具包,那么每一个启动脚本的全局代码块都需要添加工程目录,举例如下
# 当前脚本abs目录 current_script_dir = os.path.split(os.path.realpath(__file__))[0] # 当前脚本目录的上级abs目录(对于本测试用例即为python工程abs目录,如果不是,可继续上溯) current_script_dir_parent = os.path.split(current_script_dir)[0] # python工程路径 python_project_dir = current_script_dir_parent if sys.path[0] != python_project_dir: sys.path.insert(0, python_project_dir) print("添加工程目录:" + python_project_dir) print("print ("sys.path")") print(sys.path)