Python的import语句,作用是将模块或包导入当前程序中。
import语句的语法结构定义(文法规则)如下:
import_stmt ::= "import" module ["as" identifier] ("," module ["as" identifier])*
| "from" relative_module "import" identifier ["as" identifier]
("," identifier ["as" identifier])*
| "from" relative_module "import" "(" identifier ["as" identifier]
("," identifier ["as" identifier])* [","] ")"
| "from" relative_module "import" "*"
module ::= (identifier ".")* identifier
relative_module ::= "."* module | "."+
看到这个语法结构定义及其使用的符号,个人感觉这是巴克斯范式和正则表达式的结合体。“::=”在巴克斯范式中可读作“定义为”;一条文法规则从形式上看是由“::=”连接的左右两个部分,“::=”左侧的符号称作非终结符(计算机基础学科——编译原理中的术语),就是在程序代码中不会出现的符号;“::=”右侧的符号串由一个或多个终结符和非终结符组成。编译器或解释器(Python是解释型语言,对程序解释执行)按照文法规则对程序代码进行理解并执行(编译或解释)。
上述import语句语句的文法定义中,“::=”右侧的符号串中,双引号中的符号串(字符或字符串)是在源代码中出现的关键字;中括号、小括号、以及星号是正则表达式中使用非符号。
上述语句的文法定义可从命令行获取,也可从帮助文档得到。
命令行获取方式,在命令行依次键入如下命令:
python
help()
import
在帮助文档中,查看的路径是:“The Python Language Reference”->“Simple statements”->“The import statement”。
为了加深印象,将上述英文部分翻译为中文。个人感觉理解这些帮助文档还是需要有一定的理论和实践基础的,翻译过程中有不少困难需要克服。需要对操作系统、编译原理、数据结构等有一定的了解:用户要求操作系统执行Python程序,操作系统按Python程序的要求创建Python进程,Python进程(类似于与现实社会中的组织、机构)中有一个至关重要的名为Python解释器的存在,Python解释器完成对用户输入的命令或程序的解释执行,使用了一个名为名空间(namespace)的数据结构。
以下为译文(译文中动词的执行者应该是Python解释器)。水平有限,仅供参考。
基本的import语句(没有from子句)分两步执行:
- 查找一个模块,必要时加载该模块并进行初始化
- 在本地名空间的import语句的作用范围内定义一个或多个名称。
当语句包含多个子句(用逗号分隔)时,将为每个子句分别执行这两个步骤,就像这些子句已被分隔为单独的导入语句一样。
关于第一步(查找和加载模块)的更详细的描述可参见“import系统”(import system),“import系统”还描述了可导入的各种类型的包和模块,以及可用于定制导入系统的所有挂钩(hooks)。
请注意,若此步骤失败,则表示无法定位模块或者在初始化模块时发生错误(包括执行模块代码时发生的错误)。
如果成功检索到被请求的模块,将通过以下三种方式之一使得该模块在本地名空间中可用:
•如果模块名称后接as,则as后的名称将直接绑定到导入的模块。
•如果未指定其他名称,并且要导入的模块是顶级模块,则该模块的名称将绑定在本地命名空间中,作为被导入模块的引用。
•如果要导入的模块不是顶级模块,则包含该模块的顶级包的名称将绑定在本地命名空间中,作为对顶级包的引用。必须使用被导入的模块的完整限定名访问该模块,而不能直接访问。
对from结构的处理要复杂一些:
- 查找from子句中指定的模块,必要时加载并初始化;
- 对于import子句中指定的每个标识符:
(1)检查被导入的模块是否具有与标识符同名的属性;
(2)若没有,则尝试导入具有该名称的子模块,然后再次检查被导入的子模块的同名属性;
(3)如果未找到属性,则产生导入错误(ImportError)。
(4)如果找到,则将一个对该值的引用存储在本地命名空间中;引用名的确定规则是:如果存在as子句,则使用其中的名称,否则使用属性名称。
例子:
import foo # foo imported and bound locally (foo被导入,与本地名foo绑定)
import foo.bar.baz # foo.bar.baz imported, foo bound locally (foo.bar.baz被导入,foo与本地名foo绑定)
import foo.bar.baz as fbb # foo.bar.baz imported and bound as fbb (foo.bar.baz被导入,并与名fbb绑定)
from foo.bar import baz # foo.bar.baz imported and bound as baz (foo.bar.baz被导入,与名baz绑定)
from foo import attr # foo imported and foo.attr bound as attr (foo被导入,并将foo.attr与名attr绑定)
如果标识符列表被星号(“*”)替换,则模块中定义的所有公共名称都将绑定到import语句所在范围的本地命名空间中。
模块定义的公共名称是通过检查模块名称空间中名为__all__的变量来确定的,如果定义了该变量,则它必须是由该模块定义或导入的名称组成的字符串序列。__all__中给出的名称都被视为公共名称,并且必须存在。如果没有定义变量__all__,公共名称集包括在模块命名空间中找到的所有名称,这些名称不以下划线字符开头('_')。__all__应包含所有的公共API。它旨在避免意外导出不属于API的项目(例如导入并在模块中使用的库模块)。
仅允许在模块级别使用通配符形式的导入(from module import *)。试图在类或函数定义中使用它将引发语法错误。
指定要导入的模块时,不必指定模块的绝对名称。当模块或包包含在另一个包中时,可以在同一个顶层包中进行相对导入,而不必提及包名称。通过在from之后的指定模块或包中使用前导点(leading dots ),可以指定向上遍历当前包层次结构的高度,而无需指定确切的名称。一个前导点表示被导入的模块所在的当前包。两个点表示上一个包级别。三点是上升两级,等等。
所以,如果你从一个pkg包的模块中执行“from . import mod”,将会导致导入“pkg.mod”。如果你在“pkg.subpkg1”中执行“from ..subpkg2 import mod ”将会导致导入“pkg.subpkg2.mod”。相对导入的规范包含在“Package Relative Imports ”部分。
importlib.import_module(),用于支持动态确定要加载的模块的应用程序。