Python的import机制,,模块与包在了解 im
Python的import机制,,模块与包在了解 im
模块与包
在了解 import 之前,有两个概念必须提一下:
模块: 一个.py文件就是一个模块(module)包:__init__.py文件所在目录就是包(package)当然,这只是极简版的概念。实际上包是一种特殊的模块,而任何定义了__path__属性的模块都被当做包。只不过,咱们日常使用中并不需要知道这些。
两种形式的 import
import有两种形式:
import ...from ... import ...两者有着很细微的区别,先看几行代码。
from string import ascii_lowercaseimport stringimport string.ascii_lowercase
运行后发现最后一行代码报错:ImportError: No module named ascii_lowercase,意思是:“找不到叫 ascii_lowercase 的模块”。第 1 行和第 3 行的区别只在于有没有from,翻翻语法定义发现有这样的规则:
import ...后面只能是模块或包from ... import ...中,from后面只能是模块或包,import后面可以是任何变量可以简单的记成:第一个空只能填模块或包,第二个空填啥都行。
import 的搜索路径
提问,下面这几行代码的输出结果是多少?
import stringprint(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 与 绝对 import
相对 import
当项目规模变大,代码复杂度上升的时候,我们通常会把一个一个的.py文件组织成一个包,让项目结构更加清晰。这时候 import 又会出现一些问题,比如:一个典型包的目录结构是这样的:
string/├── __init__.py├── find.py└── foo.py
如果string/foo.py的代码如下:
# string/foo.pyfrom string import findprint(find)
那么python string/foo.py的运行结果会是下面的哪一个呢?
<module ‘string.find‘ from ‘string/find.py‘><function find at 0x123456789>按我们前面讲的各种规则来推导,因为foo.py所在目录string/没有string模块(即string.py),所以 import 的是标准库的string,答案是后者。不过,如果你把foo当成string包中的模块运行,即python -m string.foo,会发现运行结果是前者。同样的语句,却有着两种不同的语义,这无疑加重了咱们的心智负担,总不能每次咱们调试包里的模块时,都去检查一下执行的命令是python string/foo.py还是python -m string.foo吧?
相对 import 就是专为解决「包内导入」(intra-package import)而出现的。它的使用也很简单,from的后面跟个.就行:
# from string/ import find.pyfrom . import find# from string/find.py import *from .find import *
我们再看个复杂点的例子,有个包的目录结构长这样:
one/├── __init__.py├── foo.py└── two/ ├── __init__.py ├── bar.py └── three/ ├── __init__.py ├── dull.py └── run.py
from . import dullfrom .. import barfrom ... import fooprint(‘Go, go, go!‘)
改成
from .dull import *from ..bar import *from ...foo import *print(‘Go, go, go!‘)
结果是一样的。
那么python string/foo.py和python -m string.foo的运行结果又是怎样呢?运行一下发现,两者的输出分别是:
Traceback (most recent call last): File "string/foo.py", line 1, in <module> from . import findValueError: Attempted relative import in non-package
<module ‘string.find‘ from ‘string/find.py‘>
原因在于python string/foo.py把foo.py当成一个单独的脚本来运行,认为foo.py不属于任何包,所以此时相对 import 就会报错。也就是说,无论命令行是怎么样的,运行时 import 的语义都统一了,不会再出现运行结果不一致的情况。
Python的import机制
评论关闭