详解Python import机制(二):绝对导入与相对导入

简介

我们接着讨论 import 的相关机制,本文会着重讨论绝对导入与相对导入的概念。

import 运行流程

当我们使用 importos 时,import 是怎么工作的?

Python 首先会在 sys.modules 中搜索名为 os 的模块,如果 sys.modules 缓存中存在,则将缓存映射的内容直接返回,导入流程结束。

如果缓存中没有 os 模块,Python 会继续搜索其内置模块列表,这些模块是 Python 预先安装的,简单理解就是 Python 的标准库,os 就是标准库,所以 import 流程至此结束。

如果在内置模块中依旧没有找到,则会在 sys.path 列表定义的路径中搜索该模块。

当 Python 搜到到模块时,会在本地作用域内初始化相应的 module 对象,这样,我们在当前文件中使用该模块就不会报错了。

Python 的 import 机制非常灵活,这也带来了一些严重的安全风险,如利用导入机制重写 Python 的核心功能或利用导入机制执行恶意代码。

恶意开发者会将自己的恶意第三方包提交到 PyPi 供人下载,这些包名通常与流行的包名相似,功能也相似,只是多些恶意代码,比如我们常用从 requests 来实现 HTTP 的 GET 与 POST 请求,那么恶意开发者可以将 requests 的源码 download 下来,添加恶意代码,然后修改为 request 提交到 PyPi,恶意代码可以利用导入机制重写 Python,从而获得你设备一定程度的控制权。

为了避免这种情况,你只能在安装第三方库时,确认好第三方库的名称。

安全与灵活就像鱼和熊掌,是不可兼得的。

import 语句的标准写法

在编写 import 语句时,无论你使用使用相对导入还是绝对导入,都应该遵循 PEP 8 中提及的建议,这会让代码看起来更加优雅

1. 导入语句应卸载文件的顶部,但在模块 (.py 文件) 注释或说明文字之后

2. 导入语句要根据导入内容的不同进行分类,一般将其分为 3 类。第一类:导入 Python 内置模块。第二类:导入相关的第三方库模块 第三类:导入程序本地的模块 (即当前应用的模块)

3. 导入不同类别模块时,需要使用空行分开

标准如下:

"""这是PEP 8建议的导入模块标准"""
# 内置模块
import os
import time
# 第三方模块
import flask
# 本地模块
from test import test1

绝对导入 Absolute Import

无论是绝对导入还是相对导入,都需要一个参照物,不然「绝对」与「相对」的概念就无从谈起。

绝对导入的参照物是项目的根文件夹。

绝对导入要求我们使用从项目的根文件夹到要导入模块的完整路径。

假设我们有如下目录结构:

└── project
    ├── package1
    │   ├── module1.py
    │   └── module2.py
    └── package2
        ├── __init__.py
        ├── module3.py
        ├── module4.py
        └── subpackage1
            └── module5.py

根文件夹为 project,它下面有 package1 与 package2 这两个文件夹,其中 package1 下有 module1.py 与 module2.py 这两个模块,而 package2 下有__init__.py 以及 module3.py 与 module4.py 这 3 个模块,此外还有 subpackage1 目录,其下包含 module5.py 模块。

package1/module2.py 中包含一个名为 function1 的函数。package2/__init__.py 中包含一个名为 calss1 的类。package2/subpackage1/module5.py 中包含一个名为 function2 的函数。

那么使用绝对路径导入的实例如下:

from package1 import mudule1
from package1.module2 import function1
from package2 import class1
from package2.subpackage1.module5 import function2

「绝对路径要求我们必须从最顶层的文件夹开始,为每个包或每个模块提供出完整详细的导入路径。」

在 Python3.x 中,绝对导入是默认的导入形式,也是 PEP 8 推荐的导入形式,它相当直观,可以很明确的知道要导入的包或模块在哪里。

此外「使用绝对导入的模块 (.py 文件) 其路径位置发生了改变,绝对导入依旧生效」,但如果绝对导入的对象路径发生了变化,此时就需要改写绝对导入语句了。

但有时,绝对导入显得冗长复杂,这个时候就可以尝试使用相对导入

相对导入

相对导入的参照物为当前位置,当我们使用相对导入时,需要给出相对与当前位置,想导入资源所在的位置。

细究而言,其实有两种相对导入,分别是「隐式相对导入」与「显示相对导入」,简单提一嘴。

依旧是如下目录结构

└── project
    ├── package1
    │   ├── module1.py
    │   └── module2.py
    └── package2
        ├── __init__.py
        ├── module3.py
        ├── module4.py
        └── subpackage1
            └── module5.py

package1/module2.py 中包含一个名为 function1 的函数。package2/__init__.py 中包含一个名为 calss1 的类。package2/subpackage1/module5.py 中包含一个名为 function2 的函数。

那么在 package2/module3.py 中引用 module4.py,其写法如下

# package2/module3.py
import module4 # 隐式相对导入
from . import module4 # 显式相对导入
from package2 import module4 # 绝对导入

所谓「隐式相对导入」就是不显式的告知 Python 相对于当前位置进行导入,这种方式在 Python3.x 中已经不推荐使用,主要原因就是「隐式相对导入」不够明了。

如果你还在使用 python2.x,可以通过 from__future__importabsolute_import禁用隐式相对导入。

如果你想在 package1/module1.py 中使用 function1,使用相对导入可以这样写

# package1/module1.py
from .module2 import function1 # 因为module2与module1在同一个目录中,所以使用.module2

如果你想在 package2/module3.py 中导入 class1 和 function2,使用相对导入可以这样写

# package2/module3.py
from . import class1 # 单点,表示从当前包中导入 class1,具体而言就是从__init__.py中导入class1
from .subpackage1.module5 import function2 # 因为subpackage1与module3.py在同一目录中,所用使用.subpackage1

相对导入相较于绝对导入而言,通常更加简洁,但对于一些目录结构可能会发生变化的项目而言,相对导入没有那么直观,可能会产生混乱。

本篇文章比较快速的介绍了 import 中相对导入与绝对导入相关概念。

如果本文对你有所帮助,请点击「好看」支持一下,这对本小号很重要。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

懒编程-二两

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值