返回介绍

相对导入的应用

发布于 2024-01-29 22:24:15 字数 4380 浏览 0 评论 0 收藏 0

理论介绍足够了,让我们来看一些快速测试,以展示相对导入背后的概念。

在包之外导入

首先,正如前面所提到的,这一功能不会影响到一个包之外的导入。因此,如下的代码像预期的那样查找标准库string模块。

但是,如果在所工作的目录中添加一个同名的模块,将会选择该模块,因为模块搜索路径的第一条是当前工作目录(CWD)。

换句话说,常规的导入仍然相对于“主”目录(顶级的脚本的包含目录,或者我们正在工作的目录)。实际上,如果不是在用作一个包的部分的一个文件中,甚至不允许相对导入语法。

在本节的这个以及所有的示例中,在交互提示模式中输入的代码与将其放在一个顶层脚本中运行的行为是相同的,因为sys.path上的第一个条目要么是交互的工作目录,要么是包含顶层文件的目录。唯一的区别是,sys.path的开始是一个绝对路径,而不是一个空字符串。

包内的导入

现在,让我们来去除在CWD中编写的本地string模块,并构建带有两个模块的一个包目录,包括必需空的test\pkg\__init__.py文件(在这里省略了)。

这个包中的第一个文件试图用一条常规的import语句导入第二个文件。由于这在Python 2.6中当作相对的,而在Python 3.0中当做绝对的,因此,后者中将会失败。也就是说,Python 2.6首先搜索包含的包,但是Python 3.0不这么做。这是在Python 3.0中必须注意的非兼容行为。

为了使得这在Python 2.6和Python 3.0下都有效,使用特殊的相对导入语法来修改第一个文件,以便其导入在Python 3.0中也搜索包目录。

导入仍然是相对于CWD的

注意,在前面的示例中,包模块仍然必须访问string这样的标准库模块。实际上,它们的导入仍然相对于模块搜索路径上的条目,即便这些条目自身是相对的。如果我们再次向CWD添加了一个string模块,包中的导入将会在那里找到它,而不是在标准模块库中。尽管我们可以在Python 3.0中略过带有一个绝对导入的包目录,我们仍然不能略过导入包的程序的主目录。

使用相对导入和绝对导入选择模块

为了展示这如何应用于标准模块库的导入,再一次重新设置包。去除掉本地string模块,并在包自身之中定义一个新的。

现在,获得哪个版本的string模块取决于你使用哪个Python版本。和前面一样,Python 3.0把第一个文件中的导入解释为绝对的并略过了该包,但Python 2.6不会这么做。

在Python 3.0中使用相对导入语法会迫使再次搜索包,就好像在Python 2.6中一样——通过在Python 3.0中使用绝对和相对导入语法,我们可以显式地跳过或选择包目录。实际上,这是Python 3.0方式所解决的情况。

相对导入语法实际上是一种绑定声明,而不只是一种偏好,注意到这点很重要。如果我们删除了这个例子中的string.py文件,string.py中的相对导入在Python 3.0和Python 2.6中都会失败,而不是回归到这一模块(或任何其他模块)的标准库版本。

相对导入所引用的模块必须在包目录中存在。

导入仍然是相对于CWD的

尽管绝对导入允许我们略过包模块,它们仍然依赖于sys.path上的其他部分。为了最后进行一次测试,让我们定义自己的两个string模块。在下面的代码中,CWD中有一个为该名称的模块,包中有一个该名称的模块,并且,另一个string模块位于标准库中。

当我们使用相对导入语法导入string模块时,我们如愿地得到了包中的那个版本。

使用绝对语法时,我们得到了不同的版本。Python 2.6将这条语句解释为相对于包的,但Python 3.0将其看做“绝对的”,在这种情况下,真的意味着它略过包并载入相对于CWD的版本(而不是相对于标准库的版本)。

你可以看到,尽管包显式地要求目录中的模块,它们的导入仍然是相对于常规模块搜索路径的剩余部分。在这个例子中,程序中的一个文件使用的包隐藏了该包可能想要的标准库模块。在Python 3.0中真正发生的变化只是允许包代码在包的内部或外部选择文件。由于导入方法可能依赖于无法预见的一个封闭环境,Python 3.0中的绝对导入并不能保证找到标准库中的模块。

自行用这些例子进一步体验。实际上,并不像宣传的那样。在开发中,你通常可以构建自己的导入、搜索路径和模块名称,使其按预想的工作。然而,我们应该记住,在一个较大系统中的导入可能取决于所使用的环境,并且模块导入协议是一个成功的库设计的一部分。

注意:既然已经学习了有关包相对导入的知识,还要记住,它们不总是你的最佳选择。绝对包导入,相对于sys.path上的一个路径,有时候在Python 2的隐式包相对导入以及Python 2和3的显式包相对导入语法中,绝对导入都是首选。

包相对导入语法和Python 3.0的新的绝对导入搜索规则,至少从一个包的相对导入要显式化,并且由此更容易理解和维护。使用带点号的导入的文件,显式地绑定到一个包目录,并且需要做代码修改才能随处使用。

当然,这对于你的模块的影响程度可能随每个包而不同,当识别目录时,绝对导入也可能需要修改。

为什么要在意:模块包

现在,包是Python的标准组成部分,常常见到较大的第三方扩展都是以包目录的形式分发给用户,而不是单纯的模块列表。例如,win32all这个Python的Windows扩展包,是首先采用包这种潮流的扩展之一。其许多工具模块都位于包内,并且通过路径来导入。例如,需要加载客户端COM工具,就需要使用如下的语句:

这一行代码会从win32com包(一个安装子目录)的client模块取出一些变量名。

包导入在Jython(Python的Java实现版本)所运行的代码中也很普遍,因为Java库也是组织为层次结构的。在最近的Python版本中,email和XML工具,也像这样组织了标准库内的子目录,并且Python 3.0组甚至与包中的模块更为相关(包括tkinter GUI工具、HTTP网络连接工具等)。如下的语句导入了对Python 3.0中的各种标准库工具的访问:

无论你是否将要建立包的目录,最终可能还是从包的目录导入的。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文