Python导入语句的权威指南!

点击上方关注,All in AI中国

我几乎从来没有能够在第一时间编写正确的Python导入语句。

Python 2.7和Python 3.6(我在这里测试的两个版本)之间的行为不一致,并且没有一种方法可以保证导入始终有效。这篇文章是我如何解决常见的导入问题。除非另有说明,否则此处的所有示例均适用于Python 2.7和3.6。

目录

摘要/要点

基本定义

示例目录结构

什么是 import?

Python import和sys.path的基础知识

有关sys.path的更多信息

所有的__init__.py

将脚本文件夹转换为可导入的模块包

运行包初始化代码

使用导入的模块或包中的对象

使用dir()检查导入模块的内容

导入包

绝对与相对导入

案例

案例1:提前知道sys.path

案例2:sys.path可能会改变

案例3:sys.path可以改变(取2)

案例4:从父目录导入

Python 2与Python 3

摘要/要点

  • import语句搜索sys.path中的路径列表
  • sys.path始终包含在命令行上调用的脚本的路径,并且与命令行上的工作目录无关。
  • 导入包在概念上与导入该包的__init__.py文件相同

基本定义

  • 模块:模块的名字就是不带 .py 后缀的文件名
  • 内置模块:一个编译到Python解释器中的"模块"(用C编写),因此没有* .py文件。
  • 包:包含名为__init__.py的文件的任何文件夹,它的名称是文件夹的名称。在Python 3.3及更高版本中,任何文件夹(即使没有__init__.py文件)都被视为包
  • 对象:在Python中,几乎所有东西都是对象 - 函数,类,变量等。

示例目录结构

Python导入语句的权威指南!

请注意,我们不会在根测试/文件夹中放置__init__.py文件。

什么是import?

导入模块时,Python会运行模块文件中的所有代码。导入包时,如果存在此类文件,Python将运行包的__init__.py文件中的所有代码。模块中定义的所有对象或包的__init__.py文件都可供导入程序使用。

Python import和sys.path的基础知识

根据Python文档,以下是import语句如何搜索要导入的正确模块或包:

导入名为spam的模块时,解释器首先搜索具有该名称的内置模块。如果未找到,则会在变量sys.path给出的目录列表中搜索名为spam.py的文件。 sys.path从这些位置初始化:

  • 包含输入脚本的目录(或未指定文件时的当前目录)。
  • PYTHONPATH(目录名列表,语法与shell变量PATH相同)。
  • 依赖于安装的默认值。

初始化后,Python程序可以修改sys.path。包含正在运行的脚本的目录位于搜索路径的开头,位于标准库路径之前。这意味着将加载该目录中的脚本,而不是库目录中的同名模块。

资料来源:Python 2和3

从技术上讲,Python的文档是不完整的。解释器不仅会查找名为spam.py的文件(即模块),还会查找名为spam的文件夹(即包)。

请注意,Python解释器首先搜索内置模块列表,这些模块是直接编译到Python解释器中的模块。此内置模块列表与安装有关,可以在sys.builtin_module_names(Python 2和3)中找到。一些通常包含的内置模块是sys(总是包括)、math、itertools和time等。

与搜索路径中的第一个内置模块不同,Python标准库(非内置函数)中的其余模块位于当前脚本的目录之后。这会导致令人困惑的行为:可以"替换"Python标准库中的一些但不是所有模块。例如,在我的计算机上(Windows 10,Python 3.6),数学模块是内置模块,而随机模块则不是。因此,在start.py中导入数学将从标准库导入数学模块,而不是我自己的math.py文件在同一目录中。但是,在start.py中导入随机将导入我的random.py文件,而不是标准库中的随机模块。

此外,Python导入区分大小写,这是不同的。

函数pkgutil.iter_modules(Python 2和3)可用于获取给定路径中所有可导入模块的列表:

Python导入语句的权威指南!

来源

如何在python中获取内置模块列表?

(https://stackoverflow.com/questions/8370206/how-to-get-a-list-of-built-in-modules-in-python)

感谢etene指出内置模块与Python标准库中的其他模块之间的区别(问题2)

(https://github.com/etene)

有关sys.path的更多信息

Python导入语句的权威指南!

要查看sys.path中的内容,请在解释器中或作为脚本运行以下命令:Python的sys.path文档将其描述为......

一个字符串列表,指定模块的搜索路径。从环境变量PYTHONPATH初始化,加上依赖于安装的默认值。

在程序启动时初始化时,此列表的第一项path [0]是包含用于调用Python解释器的脚本的目录。如果脚本目录不可用(例如,如果以交互方式调用解释器或者从标准输入读取脚本),则路径[0]为空字符串,它首先将Python引导到当前目录中的搜索模块。请注意,在作为PYTHONPATH的结果插入条目之前插入了脚本目录。

资料来源:Python 2和3

Python命令行界面的文档添加了有关从命令行运行脚本的以下内容。具体来说,当运行python <script> .py时,那么......

如果脚本名称直接引用Python文件,则包含该文件的目录将添加到sys.path的开头,并且该文件将作为主模块执行。

资料来源:Python 2和3

让我们回顾一下Python搜索要导入的模块的顺序:

  1. Python标准库中的模块(例如math,os)
  2. sys.path指定的目录中的模块或包:

·如果Python解释器以交互方式运行:

sys.path [0]是空字符串''。这告诉Python搜索启动解释器的当前工作目录,即Unix系统上的pwd输出。

如果我们使用python <script> .py运行脚本:

sys.path [0]是<script> .py的路径

·PYTHONPATH环境变量中的目录

·默认的sys.path位置

请注意,在运行Python脚本时,sys.path不关心当前的"工作目录"是什么。它只关心脚本的路径。例如,如果我的shell当前位于test /文件夹中并运行python ./packA/subA/subA1.py,那么sys.path包含test / packA / subA /但不是test /。

此外,sys.path在所有导入的模块之间共享。例如,假设我们调用python start.py。让start.py导入packA.a1,让a1.py打印出sys.path。然后sys.path将包含test /(start.py的路径),但不包括test / packA /(a1.py的路径)。这意味着a1.py可以调用import,因为other.py是test /中的文件。

所有的__init__.py

__init__.py文件有2个函数。

  1. 将脚本文件夹转换为可导入的模块包(在Python 3.3之前)
  2. 运行包初始化代码

将脚本文件夹转换为可导入的模块包

为了从与我们正在编写的脚本不同的目录(或者我们运行Python交互式解释器的目录)中导入模块或包,该模块需要位于包中。

如上所述,具有名为__init__.py的文件的任何目录都是Python包。此文件可以为空。例如,在运行Python 2.7时,start.py可以导入包packA但不能导入packB,因为test / packB /目录中没有__init__.py文件。

由于采用了隐式命名空间包,因此不适用于Python 3.3及更高版本。基本上,Python 3.3+将所有文件夹视为包,因此不再需要空__init__.py文件,可以省略。

例如,packB是命名空间包,因为它在文件夹中没有__init__.py文件。如果我们在test /目录中启动Python 3.6交互式解释器,那么我们得到以下输出:

Python导入语句的权威指南!

来源

1.什么是init.py?(https://stackoverflow.com/questions/448271/what-is-init-py-for)

2.PEP 420:隐式命名空间包(https://www.python.org/dev/peps/pep-0420/)

运行包初始化代码

第一次导入包或其中一个模块时,如果文件存在,Python将在包的根文件夹中执行__init__.py文件。 __init__.py中定义的所有对象和函数都被视为包命名空间的一部分。

请考虑以下示例。

test/packA/a1.py

Python导入语句的权威指南!

test/packA/__init__.py

Python导入语句的权威指南!

test/start.py

Python导入语句的权威指南!

运行python start.py的输出

Python导入语句的权威指南!

*注意:如果a1.py调用import a2并运行python a1.py,则不会调用test / packA / __ init__.py,即使a2似乎是packA包的一部分。这是因为当Python运行脚本(在本例中为a1.py)时,其包含的文件夹不被视为包。

使用导入的模块或包中的对象

编写import语句有4种不同的语法。

  1. import <package>
  2. import <module>
  3. 来自<package> import <module或subpackage或object>
  4. 来自<module> import <object>

设X是导入后的任何名称。

  • 如果X是模块或包的名称,那么要使用X中定义的对象,您必须编写X.object。
  • 如果X是变量名,那么它可以直接使用。
  • 如果X是函数名,那么可以用X()调用它

可选地,因为可以在任何导入X语句之后添加Y:将X导入为Y.这将在脚本中将X重命名为Y.请注意,名称X本身不再有效。一个常见的例子是导入numpy为np。

导入函数的参数可以是单个名称,也可以是多个名称的列表。这些名称中的每一个都可以选择通过as重命名。例如,这将是start.py中的有效import语句:import packA as pA,packA.a1,packA.subA.sa1 as sa1

示例:start.py需要在sa1.py中导入helloWorld()函数

  • 解决方案1:从packA.subA.sa1导入helloWorld

我们可以通过名称直接调用函数:x = helloWorld()

  • 解决方案2:从packA.subA导入sa1或等效导入packA.subA.sa1作为sa1

我们必须在函数名前加上模块的名称:x = sa1.helloWorld()

这有时比解决方案1更受欢迎,以便明确我们从sa1模块调用helloWorld函数。

  • 解决方案3:导入packA.subA.sa1。

我们需要使用完整路径:x = packA.subA.sa1.helloWorld()

使用dir()检查导入模块的内容

导入模块后,使用dir()函数从模块中获取可访问名称的列表。例如,假设我导入sa1。如果sa1.py定义了一个helloWorld()函数,那么dir(sa1)将包含helloWorld。

导入包

Python导入语句的权威指南!

导入包在概念上等同于将包的__init__.py文件作为模块导入。 实际上,这就是Python将包视为:

Python导入语句的权威指南!

导入器只能访问导入的包的__init__.py中声明的对象。例如,由于packB缺少__init__.py文件,因此调用import packB(在Python 3.3+中)几乎没有用处,因为packB包中没有对象可用。随后对packB.b1的调用将失败,因为它尚未导入。

绝对与相对导入

绝对导入使用完整路径(从项目的根文件夹开始)到要导入的所需模块。

相对导入使用相对路径(从当前模块的路径开始)到要导入的所需所需模块。有两种类型的相对导入:

  • 显式相对导入遵循<module / package> import X的格式,其中<module / package>以点为前缀。表示向上遍历的目录数量。一个点。对应当前目录;两个点..表示一个文件夹;等等
  • 写入隐式相对导入,就像当前的directoy是sys.path的一部分一样。隐式相对导入仅在Python 2中受支持。它们不支持PYTHON 3。

Python文档说明了Python 3对相对导入的处理:

相对导入唯一可接受的语法来自[module]导入名称。所有导入表单都不以被解释为绝对导入。

来源:Python 3.0中的新功能

例如,假设我们正在运行start.py,它导入a1,而a1又导入了other,a2和sa1。然后a1.py中的import语句如下所示:

·绝对导入:

Python导入语句的权威指南!

·显性相对导入:

Python导入语句的权威指南!

·隐性相对导入:

Python导入语句的权威指南!

请注意,对于相对导入,点。只能到达(但不包括)包含从命令行运行的脚本的目录。因此,from .. import在a1.py中无效。这样做会导致错误ValueError:尝试在顶级包之外进行相对导入。

一般而言,绝对进口优先于相对进口。它们避免了显性与隐式相对导入之间的混淆。此外,任何使用显式相对导入的脚本都无法直接运行:

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

资料来源:Python 2和3

来源

如何在python中完成相对导入(https://stackoverflow.com/questions/4655526/how-to-accomplish-relative-import-in-python)

导入语句python3的更改(https://stackoverflow.com/questions/12172791/changes-in-import-statement-python3)

案例

案例1:提前知道sys.path

如果你只调用python start.py或python other.py,那么很容易为所有模块设置导入。在这种情况下,sys.path将始终在其搜索路径中包含test /。因此,所有导入语句都可以相对于测试/文件夹编写。

例如:测试项目中的文件需要在sa1.py中导入helloWorld()函数

解决方案:从packA.subA.sa1导入helloWorld(或上面演示的任何其他等效导入语法)

案例2:sys.path可能会改变

通常,我们希望灵活地使用Python脚本,无论是直接在命令行上运行还是作为模块导入到另一个脚本中。如下所示,这是我们遇到问题的地方,特别是在Python 3上。

示例:假设start.py需要导入需要导入sa2的a2。假设start.py总是直接运行,从不导入。我们仍希望能够自己运行a2。

看起来很简单吧?毕竟,我们只需要2个导入语句:start.py中的1个和a2.py中的另一个。

问题:这显然是sys.path更改的情况。当我们运行start.py时,sys.path包含test /。当我们运行a2.py时,sys.path包含test / packA /。

start.py中的导入语句很简单。由于start.py总是直接运行而且从不导入,我们知道test /将在运行时始终在sys.path中。然后导入a2只是导入packA.a2。

a2.py中的导入语句比较棘手。当我们直接运行start.py时,sys.path包含test /,所以a2.py应该从packA.subA import sa2调用。但是,如果我们直接运行a2.py,那么sys.path包含test / packA /。现在导入将失败,因为packA不是test / packA /中的文件夹。

相反,我们可以尝试从subA导入sa2。当我们直接运行a2.py时,这可以解决问题。但是现在我们直接运行start.py会遇到问题。在Python 3下,这会失败,因为subA不在sys.path中。 (这在Python 2中可以,因为它支持隐式相对导入。)

让我们总结一下我们关于a2.py中导入语句的发现:

Python导入语句的权威指南!

为了完整起见,我还尝试使用相对导入:from .subA import sa2。这与packA.subA import sa2的结果相匹配。

解决方案(解决方法):我不知道这个问题的简洁的解决方案。以下是一些解决方法:

1.使用以test /目录为根的绝对导入(即上表中的中间列)。这可以保证直接运行start.py始终有效。要直接运行a2.py,请将其作为导入的模块而不是脚本运行:

  • 将目录更改为测试/在控制台中
  • python -m packA.a2

2.使用以test /目录为根的绝对导入(即上表中的中间列)。这可以保证直接运行start.py始终有效。为了直接运行a2.py,我们可以在导入sa2之前修改a2.py中的sys.path以包含test / packA /。

Python导入语句的权威指南!

注意:此方法通常有效。但是,在某些Python安装下,__ file__变量可能不正确。在这种情况下,我们需要使用Python内置的检查包。有关说明,请参阅此StackOverflow答案。

3.仅使用Python 2,并使用隐式相对导入(即上表中的右列)

4.使用以test /目录为根的绝对导入,并将test /添加到PYTHONPATH环境变量中。

  • 这个解决方案不便携,所以我建议不要这样做。
  • 说明:永久添加目录到PYTHONPATH

案例3:sys.path可以改变(取2)

下面的例子是一个难以处理的问题。假设a2.py永远不需要直接运行,但它由start.py和a1.py直接运行。

在这种情况下,使用上述解决方案1将不起作用。但是,其他解决方案仍然有效。

案例4:从父目录导入

如果我们不修改PYTHONPATH并避免以编程方式修改sys.path,那么以下是Python导入的主要限制:

直接运行脚本时,无法从其父目录导入任何内容。

例如,如果我要运行python sa1.py,那么forsa1.py不可能从a1.py导入任何内容而无需求助于PYTHONPATH或sys.path解决方法。

起初,似乎相对导入(例如来自.. import a1)可以解决这个限制。但是,正在运行的脚本(在本例中为sa1.py)被视为"顶级模块"。尝试从此脚本上方的文件夹导入任何内容会导致此错误:ValueError:尝试相对导入超出顶级包。

我的方法是避免编写必须从父目录导入的脚本。如果必须发生这种情况,首选的解决方法是修改sys.path。

Python 2与Python 3

上面已经记录了Python 2和Python 3如何处理导入语句之间最重要的区别。这里再次重申它们,以及其他一些不那么重要的差异。

Python 2支持隐式相对导入。 Python 3没有。

Python 2需要文件夹中的__init__.py文件,以便将该文件夹视为包并使其可导入。在Python 3.3及更高版本中,由于它支持隐式命名空间包,所有文件夹都是包,无论是否存在__init__.py文件。

在Python 2中,可以从函数中的<module> import *中编写。在Python 3中,from <module> import *语法只允许在模块级别,不再在函数内部。

资料来源:

导入语句python3的更改(https://stackoverflow.com/questions/12172791/changes-in-import-statement-python3)

Python 2模块文档(https://docs.python.org/2/tutorial/modules.html#intra-package-references)

Python 3模块文档(https://docs.python.org/3/tutorial/modules.html#intra-package-references)

Python 3.0中的新功能(https://docs.python.org/3.0/whatsnew/3.0.html)

Python导入语句的权威指南!

侵删

相关推荐