python模块导入详解在编写python代码的时候,我们经常会导入一些内置模块、第三方模块或者自己目录下编写的模块。可以通过绝对路径或相对路径导入模块。它可以导入整个包、模块或模块中的特定对象(类、函数或变量等)。在一些大型项目中,如果不按照一定的方式管理包的导入,文件之间的导入会非常混乱,容易出错。对于项目包的外部调用者,如果调用路径太深或者太乱,是很不友好的。下面构建一个package目录,后面的所有例子都是按照这个结构来解释和说明的。项目根目录下有两个package,package_1和package_2;package_1下有两个包package_1_1和package_1_2;package_2下有一个包package_2_1。module_1_1中有一个类Cls1_1;module_1_1_1中有一个类Cls1_1_1;所有其他模块也是如此;所有初始化文件当前都是空的。导入运行机制导入XX时,Python会先从sys.modules路径下寻找模块。在python中加载到内存中的所有模块都放在sys.modules缓存中。如果加载,它只是将模块的名称添加到调用导入的模块的本地名称空间中。如果在sys.modules缓存中没有找到,它会去搜索python的内置模块。如果还是没有找到,它会在sys.path列表定义的路径中寻找模块,如果找到了,就会加载到当前空间。其中sys.path主要是python设置的三方包安装路径和当前工作路径。>>>sys.path['D:\\pycharm\\PyCharmCommunityEdition2021.1.1\\plugins\\python-ce\\helpers\\pydev','D:\\pycharm\\PyCharmCommunityEdition2021.1.1\\plugins\\python-ce\\helpers\\third_party\\thriftpy','D:\\pycharm\\PyCharmCommunityEdition2021.1.1\\plugins\\python-ce\\helpers\\pydev','C:\\Users\\Administrator\\anaconda\\envs\\blog\\python38.zip','C:\\Users\\Administrator\\anaconda\\envs\\blog\\DLLs','C:\\Users\\Administrator\\anaconda\\envs\\blog\\lib','C:\\Users\\Administrator\\anaconda\\envs\\blog','C:\\Users\\Administrator\\anaconda\\envs\\blog\\lib\\site-packages',#第三方包安装路径'D:\\pycharmprojects','D:/pycharmprojects'##Currentworkingpath]这里有个值得注意的地方pycharm等一些IDE会自动将当前项目工作目录添加到sys.path中,比如上面的例子就是在pycharm中运行。比如在控制台或服务器运行代码时,不会自动添加,如:(tensorflow)D:\>cdpycharmprojects\(tensorflow)D:\pycharmprojects>python>>>importsys>>>importos>>>os.getcwd()'D:\\pycharmprojects'>>>importsys>>>sys.path['','C:\\Users\\Administrator\\anaconda\\envs\\tensorflow\\python36.zip','C:\\Users\\Administrator\\anaconda\\envs\\tensorflow\\DLLs','C:\\Users\\Administrator\\anaconda\\envs\\tensorflow\\lib','C:\\Users\\Administrator\\anaconda\\envs\\tensorflow','C:\\Users\\Administrator\\anaconda\\envs\\tensorflow\\lib\\site-packages',#三方包路径'C:\\Users\\Administrator\\anaconda\\envs\\tensorflow\\lib\\site-packages\\win32','C:\\Users\\Administrator\\anaconda\\envs\\tensorflow\\lib\\site-packages\\win32\\lib','C:\\Users\\Administrator\\anaconda\\envs\\tensorflow\\lib\\site-packages\\pythonwin']可以看到,在控制台运行sys.path时没有当前工作目录D:/pycharmprojects;所以在本地IDE运行代码时往往没有问题,但是推送到服务器运行时,却经常报没有XX包,其中一个可能的原因是服务器上的sys.path中没有添加当前项目的根目录。绝对导入绝对导入和相对导入都有引用。绝对导入的引用基础是顶层包的根目录;比如module_2的顶层包是package_2;虽然module_2_1在package_2_1下,但是package_2_1在package_2下,所以module_2_1的顶层包还是module_2_1。引用顶层包的根目录,绝对导入方式如下:fromXXXimportyyy#XXX是基于包根目录的路径,yyy是要导入的包、模块或对象在这个路径下导入示例:示例1:importmodule_2_1orCls2_1inmodule_2_1inmodule_2.pyfrompackage_2.package_2_1importmodule_2_1#module_2_1的根目录是package_2#更进一步,导入module中的classfrompackage_2.package_2_1.module_2_1importCls2_1注意相对导入和绝对导入一般都是指包内的导入。但是这里的package必须指的是顶层包,比如应该是package_2而不是package_2_1;如果使用frompackage_2_1importmodule_2_1导入也可以成功,不过这里是后面会提到的相对导入(隐式),因为module_2.py和package_2_1在同一个目录下。例2:importmodule_1_1inmodule_1_2#绝对导入,module_1_1的顶层包根目录为package_1frompackage_1.package_1_1importmodule_1_1例3:importmodule_1_1_1#module_1_1_1inmodule_1_1顶层包根目录为package_1>>>frompackage_1.package_1_1importmodule_1_1#这样没有问题#绝对导入路径错误>>>frompackage_1_1importmodule_1_1ModuleNotFoundError:Nomodulenamed'package_1_1'注意和上面说的一样的问题,虽然module_1_1和module_1_1_1都在packagepackage_1_1,但是顶层包的根目录是package_1,如果要使用绝对导入,必须从package_1开始。相对导入相对导入的引用位置是当前文件所在的目录。分为隐式相对导入和显式相对导入。示例1:importmodule_1_1importmodule1_1inmodule_1_1_1#Implicitrelativeimportfrommodule_1_1importCls1_1#Implicitrelativeimportfrom.importmodule1_1#Displayrelativeimport,但是这样会报ImportError:attemptedrelativeimportwithnoknowninthecurrentpythonversionparentpackage;可能不支持像.包裹;具体原因不确定是不再支持了还是什么,问了很多也没有解决。因此,同一个包目录下可以使用隐式相对引用,跨目录最好直接使用绝对引用。导入规范一般在一个文件中,先导入内置模块,再导入第三方包模块,最后导入项目中自己编写的模块。示例:#import内置模块importosimportdatetime#import第三方包importkerasimportpandasaspd#import自己的模块importpackage_1总结在Python3.x中,绝对导入是默认的导入形式,也是importPEP8推荐的格式。非常直观,很清楚要导入的包或模块在哪里。并且包的名称变化需要在导入的时候做相应的修改。之后项目内部的导入尽量使用绝对导入。这里有一个问题。如果包的结构很复杂,各种引用都要绝对引用,导入包的过程会很臃肿,难记。例如:在package_1外,你想从package_1中的module_1_1中导入Cls1_1对象frompackage_1.package_1_1.module_1_1importCls1_1这时候可以将各个package中的__init__文件整理一下,这样导入更方便。在python中调用包时,会先运行它下面的init文件。如果你将这个包下的一些对象引入,那么在外部调用的时候就不需要一层层调用了。示例:在package_1下的init文件中添加:frompackage_1.package_1_1.module_1_1importCls1_1那么在一个外部模块中,如果要导入module_1_1中的Cls1_1对象,只需要frompackage_1importCls1_1
