Python3中import语句的变化

198

我不理解pep-0404中的以下内容:

在 Python 3 中,包内的隐式相对导入已不可用,只支持绝对导入和显式相对导入。此外,星号导入(例如 from x import *)仅允许在模块级别代码中使用。

什么是相对导入?Python2 中还有哪些地方可以使用星号导入?请给出示例进行解释。

4个回答

302

相对导入是指您在导入当前脚本/包相关的软件包时发生的情况。

例如,考虑以下树形结构:

mypkg
├── base.py
└── derived.py

现在,您的derived.py需要从base.py获取内容。在Python 2中,您可以通过以下方式完成(在derived.py中):

from base import BaseThing

Python 3不再支持隐式导入,因为无法明确你需要相对还是绝对的base。换句话说,如果系统中安装了名为base的Python包,则会导入错误的包。

相反,它要求您使用显式导入,以基于路径的方式明确指定模块的位置。你的derived.py应该是这样的:

from .base import BaseThing

.表示从模块目录导入base,也就是说.base对应的是./base.py

类似地,还有..前缀用于向上遍历目录结构,如../(..mod对应../mod.py),然后是...向上两级(../../mod.py), 以此类推。

需要注意的是,上面列出的相对路径是相对于当前模块(derived.py)所在的目录,而不是当前工作目录。


@BrenBarn已经解释了星号导入的情况。为了完整起见,我必须说一下同样的内容 ;)

例如,你需要使用几个math函数,但你只在一个函数中使用它们。在Python 2中,您可以半懒惰:

def sin_degrees(x):
    from math import *
    return sin(degrees(x))

请注意,在 Python 2 中,它已经会触发警告:

a.py:1: SyntaxWarning: import * only allowed at module level
  def sin_degrees(x):

在现代 Python 2 代码中,你应该做某事,在 Python 3 中则必须这样做:

def sin_degrees(x):
    from math import sin, degrees
    return sin(degrees(x))

或者:

from math import *

def sin_degrees(x):
    return sin(degrees(x))

2
当运行python derived.py时,这当然会失败。 - Milo Bem
@MiloBem 是的,如果你需要两种情况都能工作,你该怎么办? - mikuszefski
遵循这种范例只会导致导入错误。我的IDE可以根据自动完成的正确工作看到我想要做什么,但Python并不关心它。 - rocksNwaves

15

有关相对导入,请参见文档。 相对导入是指从一个模块相对于该模块所在位置进行导入,而不是绝对从sys.path导入。

至于import *,Python 2允许在函数内部使用星号导入,例如:

>>> def f():
...     from math import *
...     print sqrt

在Python 2中(至少是最近版本),这一点会发出警告。在Python 3中,不再允许这样做,您只能在模块的顶层执行星号导入(不能在函数或类内部执行)。


6
为什么做出了那个决定? - Dor
1
我的猜测是,这背后的想法是来自于《Python之禅》中的PEP20:“明确优于隐晦”。在模块前加上点号可以使相对/非相对链接变得明确,从而解决可能出现的名称冲突。尽管“可读性很重要”,但会稍微受到影响。 - Pafnucy
2
不,事实上这是一个"相反的"决定,"实际性胜过纯粹性"。这是为了优化函数内部局部变量的访问而必须采取的措施,因为如果没有"import *",编译器只需通过分析代码就可以知道哪些变量是局部的,并且可以直接查找。实际上,函数甚至不使用字典进行本地存储,而是使用优化后的数组,其中变量获得唯一的索引。 - Veky

14
为了同时支持Python 2和Python 3,请使用以下明确的相对导入。它们是相对于当前模块的。它们已经从2.5版本开始得到支持。
from .sister import foo
from . import brother
from ..aunt import bar
from .. import uncle

14
在Python 3.5中,使用"import .brother"会导致语法错误。这是正常的吗?我在包含该脚本的目录中放了_init_.py文件。 - Frikster
2
import .brother 在 Python 2 和 3 中都是无效的语法。 - Rodrigo E. Principe
1
@RodrigoE.Principe,似乎是import ..uncle。已修复。哦,我在想什么呢...可能被说“尼!”的骑士分心了... - Akseli Palén

4

添加了 Michał Górny 回答中的另一种情况:

请注意,相对导入是基于当前模块的名称。由于主模块的名称始终为 "__main__",因此用作 Python 应用程序主模块的模块必须始终使用绝对导入。


这是错误的。相对导入基于__package__。而且模块级别的__package__属性是可写的,所以即使__name__ == "__main__"您可以通过设置__package__属性来指定相对导入解析的位置 - wim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接