深入探讨 Python 的 import 机制:实现远程导入模块

2020-07-08 00:00:00 导入 模块 方法 查找 加载

所谓的模块导入( import ),是指在一个模块中使用另一个模块的代码的操作,它有利于代码的复用。

在 Python 中使用 import 关键字来实现这个操作,但不是的方法,还有 importlib.import_module()__import__() 等。

也许你看到这个标题,会说我怎么会发这么基础的文章?

与此相反。恰恰我觉得这篇文章的内容可以算是 Python 的进阶技能,会深入地探讨并以真实案例讲解 Python import Hook 的知识点。

当然为了使文章更系统、全面,前面会有小篇幅讲解基础知识点,但请你有耐心的往后读下去,因为后面才是本篇文章的精华所在,希望你不要错过。



1. 导入系统的基础

1.1 导入单元构成

导入单元有多种,可以是模块、包及变量等。

对于这些基础的概念,对于新手还是有必要介绍一下它们的区别。

模块:类似 *.py,*.pyc, *.pyd ,*.so,*.dll 这样的文件,是 Python 代码载体的小单元。

还可以细分为两种:

  • Regular packages:是一个带有 __init__.py 文件的文件夹,此文件夹下可包含其他子包,或者模块
  • Namespace packages

关于 Namespace packages,有的人会比较陌生,我这里摘抄官方文档的一段说明来解释一下。

Namespace packages 是由多个 部分 构成的,每个部分为父包增加一个子包。 各个部分可能处于文件系统的不同位置。 部分也可能处于 zip 文件中、网络上,或者 Python 在导入期间可以搜索的其他地方。 命名空间包并不一定会直接对应到文件系统中的对象;它们有可能是无实体表示的虚拟模块。

命名空间包的 __path__ 属性不使用普通的列表。 而是使用定制的可迭代类型,如果其父包的路径 (或者高层级包的 sys.path) 发生改变,这种对象会在该包内的下一次导入尝试时自动执行新的对包部分的搜索。

命名空间包没有 parent/__init__.py 文件。 实际上,在导入搜索期间可能找到多个 parent 目录,每个都由不同的部分所提供。 因此 parent/one 的物理位置不一定与 parent/two 相邻。 在这种情况下,Python 将为的 parent 包创建一个命名空间包,无论是它本身还是它的某个子包被导入。

1.2 相对/导入

当我们 import 导入模块或包时,Python 提供两种导入方式:

  • 相对导入(relative import ):from . import B 或 from ..A import B,其中.表示当前模块,..表示上层模块
  • 导入(absolute import):import foo.bar 或者 from foo import bar

你可以根据实际需要进行选择,但有必要说明的是,在早期的版本( Python2.6 之前),Python 默认使用的相对导入。而后来的版本中( Python2.6 之后),都以导入为默认使用的导入方式。

使用路径和相对路径各有利弊:

  • 当你在开发维护自己的项目时,应当使用相对路径导入,这样可以避免硬编码带来的麻烦。
  • 而使用路径,会让你模块导入结构更加清晰,而且也避免了重名的包冲突而导入错误。

1.3 导入的标准写法

在 PEP8 中对模块的导入提出了要求,遵守 PEP8规范能让你的代码更具有可读性,我这边也列一下:

  • import 语句应当分行书写
# bad
import os,sys

# good
import os
import sys

相关文章