目录
● 简要概括
○ 工作原理
○ 语法
○ 规范
● 绝对导入
○ 语法和实例
○ 优缺点
● 相对导入
○ 语法和实例
○ 优缺点
● 总结
如果你曾写过包含有一个文件以上的项目,你可能会使用过语句。对于写过多个项目的Python老鸟来说,导入import也会让他们费解。而你正在阅读这篇教程的原因有可能是因为你想要更深入的了解Python的导入 ,特别是在绝对和相对导入方面。
通过阅读本教程,你将能了解到这两种导入方式的不同之处,以及其优缺点。让我们开始吧!
简要概括
你需要对Python的,模块module和包package有一个良好的理解才能更好的明白导入的工作原理。一个Python模块实际上就是一个文件,它的文件格式为。一个Python包就是一个文件夹folder,里面含有模块文件(在Python 2中,这个文件夹要有一个文件)。
当你在一个模块中写的代码需要使用到其他模块或者包的代码要怎么办?导入它!
工作原理
但是这个导入到底是如何工作的呢?假如你导入了一个模块,代码如下:
importabc
Python首先要做的就是在中查找这个名称。它是先前曾被导入过的所有模块的缓存。简单来说,就是之前被导入过的模块,都被暂时保存在里面。
如果在这个缓存中找不到这个名称,Python接下来会去内置的模块built-in modules中查找。这些内置模块是Python预装的,可以在Python标准库中查看详情。假如还是没能在内置模块中找到它,Python将会去定义的文件夹列表中搜索。通常来说,这个列表会包括当前目录,并且会先在里面搜索。
当Python找到了模块,它会将这个模块绑定到局部作用域。这就表示当前这个已经被定义了并且在被当前文件使用而不会抛的异常。
如果这个模块名没有被找到,有就会抛出这个异常。你可以在Python的官方文档找到更多内容。
注意 :安全问题
要注意当前的导入系统有一些重大的安全风险。这是大部分是由于Python本身的灵活性。例如缓存的模块是可写并且有可能可以通过导入系统重写Python的核心函数。从第三方包中导入也会让你的应用暴露于安全隐患中。
以下是一些有用的资源关于这些安全隐患,并且关于如何解决:
10个常见的Python陷阱以及如何避免(https://hackernoon.com/10-common-security-gotchas-in-python-and-how-to-avoid-them-e19fbe265e03)
#168节:10个Python安全漏洞以及如何修补(https://talkpython.fm/episodes/show/168/10-python-security-holes-and-how-to-plug-them)
语法
现在你已经了解导入的工作原理了,让我们来看一下它的语法吧。你可以导入包和模块。(要注意一下,导入一个包的时候,实际上就是导入包里面的作为模块)你也可以从一个包或者模块中导入指定的对象。
一般有两种导入的方法。当你使用第一个,你可以直接导入该资源,如下:
importabc
可以是一个包或者模块
当你使用第二种语法,你从其他包或者模块中导入资源。看下面这个例子:
fromabcimportxyz
可以是一个模块、子包subpackage、对象object,例如类class或者函数function。
你也可以选择重命名导入的资源,如下:
importabcasother_name
这会在脚本中重命名这个已经导入的模块为。现在必须使用进行引用,不然就不被识别。
规范
PEP8 是Python的官方编码规范,里面有几个点也是适用于导入规范,以下是总结:
导入应该总是写在最前面,但需尾随任何模块单行注释和文档注释。
导入应该根据导入的内容进行分类,一般有三种:
导入标准库(Python的内置模块)
导入相关的第三方包(不属于当前应用的第三方包)
导入本地应用的模块(属于当前应用的模块)
每一组的导入都应该用空白行分隔
请看下面这个例子:
"""展示一个标准的导入规范
注意,导入语句应该位于文档注释之后
"""
# 导入标准库
importdatetime
importos
# 导入第三方库
fromflaskimportFlask
fromflask_restfulimportApi
fromflask_sqlalchemyimportSQLAlchemy
# 导入本地模块
fromlocal_moduleimportlocal_class
fromlocal_packageimportlocal_function
以上的导入语句被分成了三个部分,通过空白行分隔。并在每一个部分中,是根据字母排序的。
绝对导入
你已经了解到了如何写导入语句并且像一个专家一样知道如何写规范的导入语句。现在是时候学习关于绝对引入的内容了。
绝对导入通过使用被导入资源在项目根目录的完整路径进行导入。
语法与实例
假如你有以下目录结构:
└── project
├── package1
│ ├── module1.py
│ └── module2.py
└── package2
├── __init__.py
├── module3.py
├── module4.py
└── subpackage1
└── module5.py
这里有个目录,它包含了两个子目录:和。其中有两个文件:和。
目录有三个文件:两个模块,和,也有一个初始化文件。并且也包括一个目录,,它包含一个文件,。
我们假设以下内容:
有一个函数,叫
有一个类,叫
有一个函数,叫
以下是绝对导入的例子:
frompackage1importmodule1
frompackage1.module2importfunction1
frompackage2importclass1
frompackage2.subpackage1.module5importfunction2
要注意你必须在顶级包目录top-level package下提供每个包或者文件具体的路径。某种程度上和其文件路径相似,但是我们会使用点dot(.),而不用斜线slash(/)。
优缺点
应该优先考虑使用绝对路径,因为其更简单明了。使用它后,仅通过导入语句,就知道资源是从哪里导入的。而且,即使当前位置的导入语句改变了,绝对导入还是会保持有效。并且事实上,PEP8 推荐使用绝对导入。
当然,有时候绝对路径会变得冗长,取决于目录结构的复杂程度。想象一下以下这个语句:
frompackage1.subpackage2.subpackage3.subpackage4.module5importfunction6
这是不是很可笑?幸运的是,相对导入在这种情况下是一个绝佳的选择!
相对导入
相对导入指定了被导入资源是相对于当前的位置 - 也就是,这个位置就是导入语句所在的地方。有两种类型的相对导入:隐式和显式。隐式相对导入已经在Python3中被弃用了,所以这里我就不多讲了。
语法和实例
相对导入的语法取决于当前的位置和被导入模块、包以及对象的位置。以下是一些例子:
from.some_moduleimportsome_class
from..some_packageimportsome_function
from.importsome_class
你能看到至少有一个点在每一个导入语句的前面。相对导入利用点符号来指定位置。
单个点表示模块或者包的引用是在同一个位置的同一个目录下。两个点表示它是在当前位置的父目录中 - 意思是指上一级目录。三个点表示它位于祖父母目录中,以此类推。如果你使用类Unix系统的话,一定有熟悉的感觉!
假设你有和之前一样的目录结构:
└── project
├── package1
│ ├── module1.py
│ └── module2.py
└── package2
├── __init__.py
├── module3.py
├── module4.py
└── subpackage1
└── module5.py
这里有个目录,它包含了两个子目录:和。其中有两个文件:和。
目录有三个文件:两个模块,和,也有一个初始化文件。并且也包括一个目录,,它包含一个文件,。
重温以下内容:
有一个函数,叫
有一个类,叫
有一个函数,叫
你可以通过以下方法在导入:
# package1/module1.py
from.module2importfunction1
你在这里只需要使用一个点,因为和当前的模块是在同一个路径下面。
你也可以在中导入和通过以下方法:
在第一个导入语句中,单个点代表你从当前包中导入。要谨记,导入一个包实际上是导入包里的文件作为模块。
第二个导入语句中,你再次使用了单个点,这是因为和当前模块是在同一个目录中。
优缺点
相对导入最明显的优点就是非常简洁。取决于当前的位置,它可以将之前那可笑的冗长语句缩减到以下:
from..subpackage4.module5importfunction6
不幸的是,相对导入可能会引起混乱,特别是一些目录结构可能会改变的共享项目。且相对导入不像绝对导入那样可读,不能轻易的从导入语句中查看资源被导入的位置。
结论
好样的!你学完了这个关于绝对导入和相对导入速成课crash course。你现在已经掌握了导入的工作原理。并且你也学到了编写导入语句的最佳实践,以及了解了绝对导入和相对导入的异同之处。
现在你可以用你刚刚学到的新技能自信满满的在Python中导入标准库、第三方包和你自己的本地包了。要记住,一般情况下,你应该选择使用绝对导入而不使用相对导入,除非路径非常的复杂,不然会使导入语句冗长。
本文作者:Mbithe Nzomo
大家好,我是Mbithe! 我是一个资深的软件开发工程师,我喜欢人工智能和机器学习。我目前在曼彻斯特大学The University of Manchester读研,学习高级计算机科学Advanced Computer Science,主攻人工智能。
文章来源:https://realpython.com/absolute-vs-relative-python-imports/
领取专属 10元无门槛券
私享最新 技术干货