与单独的模块类似,模块包对应的是一个目录(而模块是对应的一个.py文件)。而包导入就是把计算机上的某个目录当成为一个命名空间,该目录下的模块名都是该命名空间中的属性。
包导入基础
在import语句中,以“.”号分割的目录作为导入模块。
import dir1.dir2.dir3.mod
在from语句中也是一样:
from dir1.dir2.dir3 import mod
这些命令中的“点号”,都是对应于机器上目录层次的路径。你可以通过dir1/dir2/dir3来查找该路径下有一个文件mod.py。
当然这个路径是基于之前介绍的搜索路径的。也就是说dir1目录是在Python程序的搜索路径下,否则会无法找到该模块。
__init__.py包文件
包导入的路径中的每个目录内都必须有__init__.py这个文件。
具体来说在这样的目录 dir0/dir1/dir2/dir3/mod.py下,在Python中执行import语句: import dir1.dir2.dir3.mod 。那么在dir1,dir2,dir3目录下都要有__init__.py文件。
总结规则如下:
- 出现在import语句之中的目录下,都应该有__init__.py文件
- dir0目录下可以没有这个文件,如果有也会被忽略。
- dir0目录必须在Python的“搜索路径”下
__init__.py可以包含Python程序代码,就像Python普通文件一样。它更像是Python包模块的声明,也可以完全是空的,可以用来明确导入的目录,而不至于混乱。更多情况下__init__.py文件扮演模块包初始化钩子、替目录产生模块命名空间以及使用目录导入时实现from*行为的角色
- 包的初始化
在Python首次导入某个目录或者包模块时,就会执行该目录下的__init__.py文件(import语句中出现的目录下的init文件都会按顺序被执行),可以用该文件来初始化文件,连接数据库等一些列初始化操作。 - 模块命名空间的初始化
包模块导入后,也会产生命名空间,而不是只是被导入的模块会有命名空间,包模块中的变量名就可以通过init文件来赋值。这类文件提供了包模块需要的命名空间。 - from… import *语句的行为
这是一个高级功能,你可以在__init__文件中定义__all__列表来定义当使用from * 语句来导入模块时,需要导出什么。from * 语句默认情况下,不会导入改包模块下的子模块,只会加载init文件定义的变量名,包括该文件明确声明了需要导入的模块。
实例
$ tree dir2
dir2
├── dir3
│ ├── __init__.py
│ └── mod.py
└── __init__.py
1 directory, 3 files
上面是目录结构,在dir2目录下的init文件中定义了一个dir2变量,而在dir3文件中定义了dir3变量:
$ cat dir2/__init__.py
dir2=222
print("dir2 init")
$ cat dir2/dir3/__init__.py
dir3=333
print("dir3 init")
$ cat dir2/dir3/mod.py
x=444
print("hello package")
>>> import dir2.dir3.mod
dir2 init #执行了dir2目录下的init文件
dir3 init #同理
hello package
>>>
导入包模块时,为了使用变量的方便都会使用from语句,因为使用import语句时,要调用包内变量你必须得要声明模块包(命名空间)。另外,如果需要当如两个目录下的同名程序时,只能使用import语句,以引入模块包来区分他们的命名空间。
>>> dir2.dir3.dir3
333
>>> from dir2 import dir2
>>> dir2 #可以不用声明命名空间直接调用
222
>>> from dir2.dir3 import dir3
>>> dir3
333
>>>
当实际需要包导入的场合,就是解决有多个同名程序文件安装到同一个机器上时,所引发的模糊性。
相对导入
如果顶层的运行文件在一个模块包的内部,这时就可以相对导入包内的相关模块。这样可以避免硬编码带来的移植问题。相对导入只适用于我们本章中已经学习过的包目录中的文件中的导入。
在Python2.6中,包模块中的文件执行导入时仍然是先从相对再绝对的搜索路径顺序,而在python3中是默认先到搜索路径中搜索模块(绝对导入),再到相对的包目录下搜索模块(相对导入),如果想要python2版本与3版本保持一致,就使用: from __future__ import absolute_import
$ tree dir2
dir2
├── __init__.py
├── main.py
└── string.py
1 directory, 5 files
$ cat dir2/main.py
import string
$ cat dir2/string.py
print("hahaha")
$ python
Python 2.7.12 (default, Nov 20 2017, 18:23:56)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import dir2.main
dir2 init
hahaha
>>> dir2.main.string #执行的是相对导入,导入的是同目录下的string
<module 'dir2.string' from 'dir2/string.pyc'>
>>>
$ cat dir2/main.py
from __future__ import absolute_import #修改main程序,加上该语句
import string
$ python
Python 2.7.12 (default, Nov 20 2017, 18:23:56)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import dir2.main
dir2 init
>>> dir2.main.string #此时是绝对导入,导入的是标准库中的模块。
<module 'string' from '/usr/lib/python2.7/string.pyc'>
>>>
$ cat dir2/import_relativity.py
from . import string #这时执行相对导入
$ python
Python 2.7.12 (default, Nov 20 2017, 18:23:56)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import dir2.import_relativity
dir2 init
hahaha
>>> dir2.import_relativity.string #相对导入,表示当前目录下的模块。
<module 'dir2.string' from 'dir2/string.pyc'>
相对导入需要注意的几点:
- 相对导入只适用于包内导入。也就是只适用于位于包内的文件中使用import语句。如果不是作用于一个包内的一个模块文件中,相对导入将会不起作用(会报错)。在Python3版本中,如果没有明确的在模块包中用from执行相对导入,那么会跳过包路径进行查找,同理相对导入只会在规定的包目录中查找模块,不会进行绝对导入。
- 相对导入只是适用于from语句。
- 术语有些含糊,相对与绝对可能会让人感到迷惑,绝对导入就是从搜索路径中开始查找模块,而相对导入时从同目录同模块包下查找模块。