乐趣区

关于python:python-模块导入详解

python 模块导入详解

在写 python 代码时常常会导入一些内置模块、第三方模块或者本人目录下写的模块。模块能够通过绝对路径或相对路径导入,既能够导入一整个包,也能够导入某个模块,还能够导入模块中的某个特定对象(类,函数或变量等)。而在一些大型的工程中,如果不通过肯定的形式治理好包的导入,则各文件之间的导入十分凌乱,极易出错。对于该工程包的内部调用者来说,如果调用的门路太深或者太乱,都很不敌对。

上面结构一个 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; 其它所有 module 同理;所有的 init 文件以后为空。

import 运行机制

当 import XX 的时候

  • Python 会首先从 sys.modules 门路中找是否有该模块。python 中所有加载到内存的模块都放在 sys.modules 缓存。如果加载了则只是将模块的名字退出到正在调用 import 的模块的 Local 名字空间中。
  • 如果 sys.modules 缓存中没找到,则搜寻 python 的内置模块
  • 如果仍没有找到,则在 sys.path 列表定义的门路中找该模块,如果找到则加载到以后空间。sys.path 下次要就是 python 设置的三方包装置的门路以及当前工作的门路。
>>>sys.path
['D:\\pycharm\\PyCharm Community Edition 2021.1.1\\plugins\\python-ce\\helpers\\pydev', 'D:\\pycharm\\PyCharm Community Edition 2021.1.1\\plugins\\python-ce\\helpers\\third_party\\thriftpy', 'D:\\pycharm\\PyCharm Community Edition 2021.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'## 当前工作门路]

这里有一个值得注意的中央,就是 pycharm 等局部 IDE 会主动增加以后的我的项目工作目录到 sys.path 中,如下面这个例子就是在 pycharm 中运行。而比方在控制台或者服务器上运行代码时,则不会主动增加,如:

(tensorflow) D:\>cd pycharmprojects\
(tensorflow) D:\pycharmprojects>python
    >>> import sys
    >>> import os
    >>> os.getcwd()
    'D:\\pycharmprojects'
    >>> import sys
    >>> 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。有了顶层包根目录的参考,则相对导入的形式如下:

from XXX import yyy
#XXX 为基于该包的根目录的门路,yyy 是该门路下要导入的包、模块或者对象

举例:

例子 1:在 module_2.py 中导入 module_2_1 或者 module_2_1 中的 Cls2_1

from package_2.package_2_1 import module_2_1#module_2_1 的根目录为 package_2
#更进一步,导入模块中的类
from package_2.package_2_1.module_2_1 import Cls2_1

留神个别说的绝对导入、相对导入都是指包内导入。但这里的包肯定是指最顶层的包,比方这里应为 package_2 而不是 package_2_1;如果应用 from package_2_1 import module_2_1 导入,也能胜利,但这里就是前面要说的绝对导入(隐式)了,因为 module_2.py 和 package_2_1 在同一目录下。

例子 2:在 module_1_2 中导入 module_1_1

# 相对导入,module_1_1 的顶层包根目录为 package_1
from package_1.package_1_1 import module_1_1

例子 3:在 module_1_1 中导入 module_1_1_1

#module_1_1_1 的顶层包根目录为 package_1
>>>from package_1.package_1_1 import module_1_1# 该形式没有问题

#该相对导入的门路谬误
>>>from package_1_1 import module_1_1
ModuleNotFoundError: No module named 'package_1_1'

留神和后面提到的同一个问题,尽管 module_1_1 和 module_1_1_1 都在包 package_1_1 下,但顶层包根目录是 package_1,如果要应用相对导入,必须从 package_1 开始。

绝对导入

绝对导入的参照地位为以后文件所在的目录。分为隐式绝对导入和显示绝对导入。

例子 1:在 module_1_1_1 中导入 module_1_1

import module1_1 # 隐式绝对导入
from module_1_1 import Cls1_1# 隐式绝对导入

from . import module1_1# 显示绝对导入,但这个在当初的 python 版本中会报错 ImportError: attempted relative import with no known parent package;或者曾经不反对. 和.. 这类绝对导入

例子 2:在 module_1_1_1 中导入 module_1_2

from ..package_1_2 import module_1_2#.. 代表下级目录 Package_1, 然而该形式仍旧报错 ImportError: attempted relative import with no known parent package;具体起因不确定是曾经不反对还是怎么,查了很多都未解决。

因而,同一个包目录下能够应用隐式绝对援用,跨目录最好间接都用相对援用。

导入标准

个别在一个文件中,先导入内置模块,再导入第三方包模块,最初导入工程内本人写的模块。

举例:

# 导入内置模块
import os 
import datetime

#导入第三方包
import keras
import pandas  as pd

#导入本人的模块
import package_1

总结

在 Python3.x 中,相对导入是默认的导入模式,也是 PEP 8 举荐的导入模式,它相当直观,能够很明确的晓得要导入的包或模块在哪里。且包的名称变动须要在导入的时候对应的批改。之后工程外部的导入尽量都是用相对导入。

这里有一个问题,如果包的构造非常复杂,各类援用又要相对援用,会让包导入的过程十分臃肿且难记,

举例:在 package_1 的内部想从 package_1 中的 module_1_1 中导入 Cls1_1 对象

from package_1.package_1_1.module_1_1 import Cls1_1

这种时候能够通过组织各 package 中的__init__文件来使导入更为不便,在 python 中当调用某包时,首先会运行其上面的 init 文件,如果在 init 中将该包上面的一些对象导入进去,在内部调用时就不必一层层调用了。

举例:

在 package_1 下的 init 文件中增加:

from package_1.package_1_1.module_1_1 import Cls1_1

那么在内部某模块中,如果想调用 module_1_1 中导入 Cls1_1 对象,则只须要

from package_1 import Cls1_1
退出移动版