0.引言
一般的包导入模式是通过以下语句执行:
import dir1.dir2.mod # . 表示路径的间隔,相当于Linux下的/ # 而mod表示的是该路径下有一个文件叫mod.py ,而这里的.py被省略了
但是在使用包导入就必须遵守一条约束:包导入语句的路径中的每个目录内都必须有__init__.py,也就是说dir1,dir2中都必须要有__init__.py这个文件。
对于以上的这种import语句,必须遵守下列规则:
- dir1和dir2中都必须包含有一个__init__.py文件
- dir0是容器,不需要__init__.py文件,如果有的话,这个文件也会被忽略。
- dir0(而非dir0dir1)必须列在模块搜索路径上(也就是此目录必须是主目录,或者列在PATHONPATH之中)
而在Pycharm中的设置是通过给File->Settings->project structure中设置root以及对容器进行Mark为source(源文件),excluded(排除)来进行标记的。
1.__init__.py
__init__.py可以包含Python 程序代码,就像普通模块文件。这类文件从某种程度上讲就像Python的一种声明,尽管如此,也可以完全是空的。作为声明,这些文件可以防止有相同名称的目录不小心隐藏在模块搜索路径中,而之后才出现真正所需要的模块。
__init__.py文件扮演了包初始化的钩子、替目录产生模块命名空间以及使用目录导入时实现from *(from ... import *)行为的角色。包可以使用其初始化文件(__init__.py)来创建所需要的数据文件,连接数据库等。
from *语句的行为:作为一个高级功能,你可以在__init__.py文件内使用__all__列表来定义目录以from * 语句形式导入时,需要导入什么。
在__init__.py文件中__all__列表是指当包(目录)名称使用from *的时候,应该导入的子模块的名称清单。如果没有设置__all__,from *语句不会自动加载嵌套于该目录内的子模块。取而代之的是,只加载该目录的__init__.py文件中赋值语句定义的变量名。包括该文件中程序代码明确带入的任何子模块。
2.相对导入
from 语句现在可以使用前面的点号(“.”)来指定,他们需要位于同一包中的模块(所谓的包相对导入),而不是位于模块导入搜索路径上某处的模块(叫绝对导入)。
比如某文件夹下存在__init__.py, a.py, b.py,如果想在b.py中导入a模块。
import b #不推荐,如果b这个名字是不存在的相同的模块,则最终还是会起到下面的相同效果 from . import b # 推荐这是相对导入的正确写法,也是强制相对导入 from .a import var1 # 从名为a的模块导入变量var
注:“.”是指的当前程序运行的路径,即相对路径;如果不使用 "."而是以"lib"之类的开头,就已经不再是相对导入了,而是绝对导入,这个时候会根据你的sys.path中的容器(左到右)来查找lib这个含有__init__.py的模块。
但是在python2.x中默认先相对再绝对的搜索顺序,而python3.x则是先绝对再相对的搜索顺序。为了保持python2和python3中的代码一致性(即编写在python2中也兼容的代码),应该加上如下语句:
from __future__ import absolute_import
相对导入的作用域:
- 相对导入适用于只在包内导入。
- 相对导入只是用于from语句。且一个from中的模块名前面有一个或者多个点号。
模块查找规则总结:
- 简单模块名(例如,A)通过搜索sys.path路径列表上的每个目录来查找,从左到右进行。这个列表由系统默认设置和用户配置设置组成。
- 包是带有特殊__init__.py文件的pychon模块的直接目录。这使得一个导入中可以使用A.B.C目录路径语法。在A.B.C的一条导入中,名为A的目录位于相对于sys.path的常规模块导入搜索,B是A中的另一个包的子目录,C是一个模块或B中的其他可导入项。
- 在一个包文件中,常规的import语句使用和其他地方的导入一样的sys.path搜索规则。包中导入使用from语句以及前面的点号,然而,它是相对于包的;也就是说,只检查包目录,并且不使用常规的sys.path查找。例如,from . import A中,模块搜索限制在包含了该语句中出现的文件的目录之中。
最后,提一个问题:在什么情况下必须通过import而不能通过from使用包?
只有在你需要读取定义在一个以上路径的相同变量名时,才必须通过import来使用包,而不能使用from。使用import,路径可以让引用独特化,然而,from却让任何变量名只有一个版本。