一直对 Python 的 import 机制不甚了解,这次在写 Easy-Karabiner 的时候就踩坑了,顺便了解了一下,发现这玩意儿还不是那么「符合直觉」,遂写篇博客讲讲。
模块与包
在了解 import 之前,有两个概念必须提一下:
模块: 一个 .py 文件就是一个模块(module)
包:__init__.py 文件所在目录就是包(package)
当然,这只是极简版的概念。实际上包是一种特殊的模块,而任何定义了__path__属性的模块都被当做包。只不过,咱们日常使用中并不需要知道这些。
两种形式的 import
import 有两种形式:
import ...from ... import ...
两者有着很细微的区别,先看几行代码。
from string import ascii_lowercase
import string
import string.ascii_lowercase
运行后发现最后一行代码报错:ImportError: No module named ascii_lowercase,意思是:“找不到叫 ascii_lowercase 的模块”。第 1 行和第 3 行的区别只在于有没有 from,翻翻语法定义发现有这样的规则:
-
import ...后面只能是模块或包 -
from ... import ...中,from 后面只能是模块或包,import 后面可以是任何变量
可以简单的记成:第一个空只能填模块或包,第二个空填啥都行。
import 的搜索路径
提问,下面这几行代码的输出结果是多少?
# foo.py
import string
print(string.ascii_lowercase)
是小写字母吗?那可不一定,如果目录树是这样的:
./
├── foo.py
└── string.py
foo.py 所在目录有叫 string.py 的文件,结果就不确定了。因为你不知道 import string 到底是 import 了 ./string.py 还是标准库的 string。为了回答这个问题,我们得了解一下 import 是怎么找到模块的,这个过程比较简单,只有两个步骤:
- 搜索「内置模块」(built-in module)
- 搜索 sys.path 中的路径
而 sys.path 在初始化时,又会按照顺序添加以下路径:foo.py 所在目录(如果是软链接,那么是真正的 foo.py所在目录)或当前目录;
环境变量 PYTHONPATH中列出的目录(类似环境变量 PATH,由用户定义,默认为空);
site 模块被 import 时添加的路径1(site 会在运行时被自动 import)。import site 所添加的路径一般是 XXX/site-packages(Ubuntu 上是 XXX/dist-packages),比如在我的机器上是 /usr/local/lib/python2.7/site-packages。同时,通过 pip 安装的包也是保存在这个目录下的。如果懒得记 sys.path 的初始化过程,可以简单的认为 import 的查找顺序是:
- 内置模块
-
.py文件所在目录 -
pip或easy_install安装的包
回到前面的问题,因为import string 是通过搜寻 foo.py 文件所在目录,找到 string.py 后 import 的,所以输出取决于import string.py 时执行的代码。