PEP 328 - 导入(Imports):多行导入和绝对/相对导入
PEP: 328
Title: Imports: Multi-Line and Absolute/Relative
Author: Aahz
from __future__ import absolute_import
你可以自由使用相对导入。Python2.6中,任何import语句如果引起包内导入将会抛出反对警告DeprecationWarning(在from <> import使用相对导入失败时该情况同样发生)。
使用圆括号导入原理
现在,如果你想从一个模块或包里面导入大量名称,你必须选择几个令人讨厌的选项之一:
- 写下长长一行并且使用反斜杠延长:
from Tkinter import Tk, Frame, Button, Entry, Canvas, Text, \
LEFT, DISABLED, NORMAL, RIDGE, END
- 写下多个import语句
from Tkinter import Tk, Frame, Button, Entry, Canvas, Text
from Tkinter import LEFT, DISABLED, NORMAL, RIDGE, END
(import * 不是一个可选的用法)
然而,应该可以使用python的标准分组机制(圆括号)来书写import 语句:
from Tkinter import (Tk, Frame, Button, Entry, Canvas, Text,
LEFT, DISABLED, NORMAL, RIDGE, END)
这部分提议一开始就得到Guido的赞成。
圆括号的支持被添加到Python2.4.
绝对导入原理
Python2.4或者更早的时候,如果你读取一个包内的模块,并不确定
import foo
是指向一个顶级模块或者另一个包内的模块。随着Python库扩展,越来越多包内模块突然意外
屏蔽标准模块。这是个相当困难的包内问题,因为没办法明确意味着哪个包。为了解决这种不
明确的情况,提议“foo”将一直被当做一个可从“sys.path”获得的模块或者包。这被成为绝
对导入。
Python开发社区选择绝对导入作为默认的导入方式是因为这是更普通常见的情况,而且绝对导
入可以提供相对导入(包内)的功能,虽然以在存档中重命名包片为更高层级或者移动一个包
到另一个包的困难为代价。
因为这代表了一种语义的变化,绝对导入将会在Python2.5和2.6中成为可选项,通过使用下
列语句:
from __future__ import absolute_import
这部分提议一开始就得到Guido的赞成。
相对导入原理
随着转移到绝对导入,相对导入是否被允许的问题产生了。一些使用情况出现了,最重要的一
种情况是相对导入能够重新整理大量包的结构而不需要编辑子包。另外,如果不使用相对导入
包内的模块不容易导入自己。
Guido认可了相对导入的想法,但是在语法上有很多反对。看起来达成了协定:相对导入会要
求列出要导入的具体名称(就是说,“import foo”作为一个赤裸术语将会永远成为一个绝对
导入的语句)。
下面是竞争者:
- 一个来自Guido:
from .foo import bar
和
from ...foo import bar
这两个形式有几个不同的语义。一个语义是让一个点代表一个层级。有很多关于数点数的抱
怨。另一个选择是只允许一个层级的相对导入。但是这样会丢掉很多功能,而且大家还会抱怨
在一个点的形式中丢掉圆点。最终选择是定义一个算法来查找相对模块和包;这块的反对原因
是“明确要比暗示好”。(提议的算法是“从当前包目录向上搜索直到最终的父包被搜到。”)
有些人建议使用其它的符号作为分隔符,比如“-”或者“^”.
还有人建议使用“*”:
from *.foo import bar
- 接下来一系列的选择从海报合并而来:
from __pkg__.__pkg__ import
和
from .__parent__.__parent__ import
许多人(包括Guido)认为这些形式太难看,但是他们看起来清晰明确。总的来说,更多人更
喜欢“pkg”作为一个更简短的选择。
- 有个建议只允许同级导入。换句话说,你不能够使用相对导入来指向更高级包树里面的模
块。你能做的要么是:
from .spam import eggs
要么是:
import .spam.eggs
- 有些人青睐于使用索引父包:
from -2.spam import eggs
这个情况,导入当前目录会很简单
from .spam import eggs
- 最后,有些人不喜欢当你想挖掘进入一个包的时候你不得不从“import”切换到“from
… import”。他们建议完全重写import语句:
from MODULE import NAMES as RENAME searching HOW
或者
import NAMES as RENAME from MODULE searching HOW
[from NAMES] [in WHERE] import ...
然而,这个不太可能在Python2.5上实施(一个太大的变化),而允许相对导入足够重要因为
我们现在需要一些东西(假设标准“import”会改变绝对导入)。更多的是,这个提议的语法有
几个未解决的问题:
- 精确的提议语法是什么?(在什么环境下?语句是什么?)
- 搜索语句绑定到什么程度?换句话说,你会写:
import foo as bar searching XXX, spam as ham searching XXX
还是:
import foo as bar, spam as ham searching XXX
Guido的决定
Guido宣布相对导入使用点号开头。单个点号开头表示一个当前包的相对导入。2个或者更多点
号表示当前包的父包,首个点号后面的每个点代表一级。下面是一个包目录布局样例:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleY.py
subpackage2/
__init__.py
moduleZ.py
moduleA.py
假设当前文件是moduleX.py或者subpackage1/init.py,新语法的正确的使用如下:
from .moduleY import spam
from .moduleY import spam as ham
from . import moduleY
from ..subpackage1 import moduleY
from ..subpackage2.moduleZ import eggs
from ..moduleA import foo
from ...package import bar
from ...sys import path
注意虽然最后一种用法合法,却不被鼓励(Guido用“不理智”形容这个用法)。
相对导入应该使用 from <> import
; import <>
会是绝对导入。当然,绝对导入可
以使用from <> import
通过忽略开头的点号。禁止使用import .foo
的原因是使
用:
import XXX.YYY.ZZZ
之后,
XXX.YYY.ZZZ
是一个可用的表达式。但是
.moduleY
不是一个可用的表达式。
相对导入和_name_参数
相对导入使用一个模块属性“name”来决定模块在包中的位置。如果模块名字不
包含包信息(比如,模块名称被设为“main”)那么相对导入被解析为该模块是
否是一个顶级模块,不管这个模块在文件系统的实际位置在哪。
相对导入和sys.modules里的间接入口
当包被引入,sys.modules里的间接入口概念开始出现。当一个包中的模块在
sys.modules里的一个入口有None值,它表示这个模块实际指向顶级模块。例
如,’Sound.Effects.string’ 在sys.modules中的值可能为None。这意味着任何解析到
这个名字的导入实际上导入的是顶级模块’string’。
这引入了一个对于相对导入准备解析一个绝对导入的优化。但是这个PEP对于绝对和
相对导入做了清晰的定义,这个优化就不被需要了。当绝对/相对导入仅仅变成可用的
导入语义时,sys.modules里的间接入口不再被支持。.
参考
更多背景知识,查看下列python开发线程:
- Re: Christmas Wishlist
- Re: Python-Dev Digest, Vol 5, Issue 57
- Relative import
- Another Strategy for Relative Import
[1] https://mail.python.org/pipermail/python-dev/2004-March/043739.html
[2] https://www.python.org/doc/essays/packages/
版权
该文档也放在了下面的公共域内:
Source: https://github.com/python/peps/blob/master/pep-0328.txt