Python模块和包导入问题详解
一、sys.path
1、当你import一个模块或者包时,Python解释器便会从sys.path中对应的路径向下递归查询(即包含子目录的查询)是否含有该模块的定义,
这里是Python文档对sys.path的定义:
sys.path
A list of strings that specifies the search path for modules. Initialized from the environment variable PYTHONPATH, plus an installation-dependent default.
指定模块搜索路径的字符串列表。由环境变量PYTHONPATH初始化,加上安装依赖的默认值。
As initialized upon program startup, the first item of this list, path[0], is the directory containing the script that was used to invoke the Python interpreter.Notice that the script directory is inserted before the entries inserted as a result of PYTHONPATH.
当程序启动时进行初始化时,该列表的第一项路径[0]是包含用于调用Python解释器的脚本的目录。注意,脚本目录是在作为PYTHONPATH结果插入的条目之前插入的。
(1)从上面我们可以看到,
sys.path是一个字符串列表
,包含了程序运行时查找模块或者包的路径,我们可以print(path)来查看当前运行时查找路径。
(2)对于可能长期用到的路径,可直接修改系统 PYTHONPATH变量。
(3)需要注意的是,path是在你每次执行该文件时进行的初始化,
且path[0]初始化为你当前运行脚本所在的目录
,其他则是Python内置模块或者虚拟环境的路径。
2、看个例子:
得到的列表如下:
二、模块和包导入问题
1、
在python 3.x之前,目录中含有__init__.py即为包
,包是对一系列py模块的集合或者说打包,想要调用包中的模块目录中必须要有__init__.py文件;而在python3.x之后去除了这一要求,意味着即使目录中没有__init__.py,也会被识别为包从而调用包中的模块。
2、下面来说说导入模块或包时的常出现的问题:
项目根目录
BASEPATH
下有a、b包和main.py,a、b包内分别有a_module、b_module模块,此外a包中还有另一个用于测试的main.py。
(1)一般来说
在根目录下的mian.py里调用a和b包内的模块(包括所有子目录模块)
或者
同级目录下调用模块
是没有任何问题的!
唯一需要注意的是在import时写明是哪个包的哪个模块。
(2)但是当你想在a包的main.py中调用 b包里的模块时问题就来了:
没找到模块b,为啥?原因在于sys.path在初始化时只包含
当前执行文件所在的目录路径
和系统环境路径(特殊时为虚拟环境里包含的路径),你执行的是
a包内的main.py
,sys.path列表的第一项便初始化为
e:\PythonCode\BathPath\a
,
可见其目录树下并没有包含 b包
。
如何解决:
法一:
其实这算不上解决办法,只是行为规范,毕竟没有人会在项目的包(比如a包)内执行主函数main,而当你在
高层目录或者项目目录下执行主函数或模块时
(
这里为BasePath下的main.py
)你根本无需担心下一层的包与包之间的调用问题。
故使用绝对或者相对路径都行,一般的:
在包内的模块间相互调用使用相对路径;包与包之间的调用使用绝对路径,当然也可以都用绝对路径。
(需要说明的是绝对路径的根目录应该是你项目最顶层的目录而不是文件的根目录)。
此时sys.path列表的第一项会初始化为
e:\PythonCode\BasePath及其递归查找到的子目录
。但要注意互相导入等问题。
例如:在a.a_module中导入b包b_module内的函数并在BasePath目录下的main.py中调用它:
法二:
用sys.path.append()即可,如果有一个自己写的包你觉得会长期用到你也可以修改环境变量PYTHONPATH加到里面去。
OK,再次运行a包内的main.py,调用成功