目录
Python 代码的目录被称为包, 因此这样的导入就称为包导入。它可以简化模块搜索目录的设置。事实上, 包导入是把计算机上的目录变成另一个 Python 命名空间, 其属性则对应于目录中所包含的子目录和模块文件。
1.包导入基础
1.1 包导入语句
如下文件目录结构:
dir0\dir1\dir2\mod. py
其对应形式的 import 语句:
import dir1. dir2. mod
from dir1. dir2. mod import x
这些语句中“带点号”的路径对应于机器上目录层次的路径,使用 from 语句可以避免每次读取时都要重新输入路径,通常这样比较方便。
目录必须遵循下列规则:
- dirl 和 dir2 中都必须含有一个__init__. py 文件。
- dir0 是容器,不需要__init__.py 文件;如果有的话,这个文件也会被忽略。
- dir0 必须列在模块搜索路径的 sys. path 列表中。
就像模块文件一样,任何已导入的目录也可以传递给 reload,来强制该项目重新执行。
>>>from imp import reload
>>>reload (dir1)
1.2 __init__.py 文件角色
包的初始化
Python 在首次导入某个目录时,会自动执行该目录下__init__.py 文件中的所有程序代码。
因此,包可以通过其初始化文件来创建所需要的数据文件、连接数据库等。
模块使用的声明
包的__init__.py 文件从某种程度上讲就是声明一个路径是 Python 包。在扮演这个角色的时候, init_. py 文件可以防止有相同名称的目录不小心隐藏了出现在模块搜索路径后面的真正模块。
模块命名空间的初始化
导入表达式 dir 1. dir 2 (注意是包)运行后, 会返回一个模块对象, 而此对象的命名空间包含了 dir 2 的 __init__. py 文件中赋值的所有名称(包含import 和 from 导入的),可对外提供简洁的接口,隐藏导入细节。
from* 语句的行为
你可以在_init_py 文件内定义__all__列表来规定目录以 from*语句形式导入时,需要导出什么。在__init__. py 文件中,__all__列表是指当包 (目录) 名称使用 from*的时候,应该导入的子模块的名称清单。如果没有设定__all__, from*语句不会自动加载嵌套于该目录内的子模块;取而代之的是,只加载该目录的 __init__.py 文件中赋值语句定义的名称,包括该文件中程序代码显式导入的任何子模块。
2.包相对导入
2.1基础
绝对导入:不以点号开头的导入,仅在sys.path路径上搜索。只有程序模式的文件 (顶层脚本) , 才能导入位于同一目录下的其他模块。
相对导入:from 语句以点号 (“.”或"..") 开头的子句的导入,只在包的内部目录搜索。
from . import spam #导入模块
from .spam import name #导入模块内变量
2.2使用限制
- 使用 from .的相对导入语法的文件本身必须是作为包的一部分 (即该文件在其他地方被导入)。
- 包相对导入语句的使用会隐式地将一个文件与一个包目录的角色进行绑定(不能当作程序目录导入,即不能当作脚本直接运行)。
2.3使用方式
针对使用限制中的第二点,可用如下方法。
解决方法一:将真正的包模块文件单独放到一个子目录中,与顶层脚本文件隔离开来。这样可以将顶级目录用作一个独立的程序目录,同时内嵌的目录也能被其他程序当作包进行使用。
解决方法二:使用完整路径的绝对导入来运行脚本。
3.命名空间包
3.1基础
- 命名空间包是一种对多个目录的虚拟拼接(导入时发生),这些目录可以位于多个 sys.path 的项目中。
- 所有能够成为一个命名空间包组成部分的目录都不能包含__init__. py 文件 。
- 一旦一个命名空间包被创建,它和一个常规包之间并没有功能上的区别。
示例:
两个名为 sub 的子目录位于两个不同的父目录 dir1 和 dir2 中:
C:\code\ns\dir1\sub\mod1. py
C:\code\ns\dir2\sub\mod2. py
如果我们将 dir1 和 dir2 都添入模块搜索路径,sub 将成为一个横跨这两个目录的命名空间包。该命名空间包含有 mod1. py 和 mod2. py 这两个模块文件。
3.2命名空间包导入算法
当对每个模块搜索路径中的 directory 搜索名为 spam 的被导入包时,Python 会按照下面的顺序测试一系列更广的匹配条件:
1.如果找到 directory\spam\__init__.py,便会导入一个常规包并返回。
2.如果找到 directory\spam.{py,pyc,或其他模块扩展名},便会导入一个简单模块并返回。
3.如果找到文件夹 directory\spam,便会将其记录下来,而扫描将从搜索路径中的下一个目录继续。
4.如果上述的所有都没有找到,扫描将从搜索路径中的下一个目录继续。
如果搜索路径扫描结束后没有从上述步骤 1 和步骤 2 中返回一个模块或包,而同时在上述步骤 3 中至少记录了一个路径,那么就会创建一个命名空间包。
命名空间包有一个 __path__属性,该属性被设置为在上述步骤 3 中扫描并记录的目录路径字符串的可迭代对象。
3.3对常规包影响:可选的__init__.py
如果一个单独目录包没有初始化文件,它将被当作一个单独目录命名空间包。但包含__init__.py的常规包,在创建和加载上有性能上优势,常规包在上述搜索步骤1就会完成。