Day 08 import 进阶

这篇主要是讲到有关 Python 中很重要的 import,因为如果後面在做大型专案时,常常需要 import 其他模组或套件进来,而 Python 的 import 比较多眉角需要注意。虽然不长但有点难理解,不过知道了之後超(少)级(踩)的(很)好(多)用(雷),所以把它独立出来做一篇了。

不过要说到进阶的 import 就要先说到 Module(模组) 跟 Package(套件) 的差异,每个 Module(模组) 其实就是一个 .py 档(忘记 .py 档是啥地去重看 Day 03 ),里面有功能相近的 Function(函式) ( Day 03 一样有讲到);一个 Package(套件) 就是里面有一堆(可有可无) Module(模组) 与一个 __init__.py 档的一个资料夹,大概会像这样:

sample_package  # 它就是 Package
├── __init__.py  # Package 里面一定要有的,里面可以为空,也可以填一些初始化的东西。
└── sample_module.py

sample_module.py

# Module 里的 Function
def sample_function():
    # Do something

大概结构会向上面一样,而 import 分为 Absolute imoprt(绝对导入)Explicit relative import(显式相对导入) 这两种类型,说到这里,还记得 Day 02 讲到的 PEP8 吗?在那个 Coding Style 里面有关於 import 的规定及建议。

那麽先看看关於 Absolute imoprt(绝对导入) 吧。PEP8 中是这麽写的(有点长,不想看可以跳到後面没关系):

Absolute imports are recommended, as they are usually more readable and tend to be better behaved (or at least give better error messages) if the import system is incorrectly configured (such as when a directory inside a package ends up on sys.path)

建议使用绝对导入,因为如果导入系统配置不正确(例如当包内的目录最终位於 sys.path 上时),它们通常更具可读性并且往往表现更好(或至少给出更好的错误消息)

好的,我们知道了 Absolute imoprt(绝对导入) 是比较建议的写法,那 Explicit relative import(显式相对导入) 又如何呢?

Explicit relative imports are an acceptable alternative to absolute imports, especially when dealing with complex package layouts where using absolute imports would be unnecessarily verbose.

显式相对导入是绝对导入的可接受替代方案,尤其是在处理复杂的套件布局时,使用绝对导入会不必要地冗长

好的,我们又知道了 Explicit relative import(显式相对导入) 也是可以接受的写法,尤其是在处理复杂的套件时。不过既然有显式,那有没有隐式呢?其实原本是有的(对,现在没有,正确地说是在 Python3 中没有),为什麽好端端的就不见了呢?PEP8中也有解释。

Implicit relative imports should never be used and have been removed in Python 3.

不应使用隐式相对导入,并且已在 Python 3 中删除。

那 Absolute imoprt(绝对导入) 跟 Explicit relative import(显式相对导入) 有什麽不一样呢?

Absolute imoprt(绝对导入) & Explicit relative import(显式相对导入)

同一个 Package

现在假设要做一个专案,你写好了一个模组(例如对资料库操作的模组) module_x.py ,跟一部分功能(例如注册、登入、登出) module_a.py (虽然放一起感觉怪怪的,但先不要理它),结构长这样:

package_1
├── __init__.py  # 它是空的
├── module_a.py  # 模组
└── module_x.py  # 功能

package_1/module_x.py 长这样

def function_x():
    # Do something

module_a.py 要导入 package_1 里的 module_x.py 使用,绝对导入跟显式相对导入要怎麽写呢?

Absolute imoprt(绝对导入)

module_a.py

from package_1.module_x import function_x
# or
from package_1 import module_x

def function_a():
    # Do function_x
    function_x()

Explicit relative import(显式相对导入)

module_a.py

from .module_x import function_x
# or
from . import module_x

def function_a():
    # Do function_x
    function_x()

94这样,看出差别了吗?

不同 Package

如果之後想把模组跟功能分开放,并且增加一个新模组,就建立另一个 Package 把他们分开。分开後长这样:

package
├── __init__.py  # 它是空的
├── subpackage_1
│   ├── __init__.py  # 它是空的
│   └── module_x.py  # 模组,它不动
├── subpackage_2
│   ├── __init__.py  # 它是空的
│   └── module_y.py  # 模组,新增的另一个模组
└── module_a.py  # 功能,它搬过来

假设 module_y.py 模组要使用到 module_x.py 模组要怎麽引入呢?

Absolute imoprt(绝对导入)

module_y.py

from package.subpackage_1.module_x import function_x
# or 
from package.subpackage_1 import module_x

def function_a():
    # Do function_x
    function_x()

Explicit relative import(显式相对导入)

module_y.py

from .subpackage_1.module_x import function_x
# or 
from .subpackage_1 import module_x

def function_a():
    # Do function_x
    function_x()

说到这里,应该对 import 有更进一步的认识了吧!

要注意的点

最後是有一些要注意的东西

  1. 包含显式相对导入的档案不能直接执行,只能作为 module 被引用。

    直接执行

     $ python abc/xyz.py
    

    没有办法直接执行可是又一定要执行怎麽办,全部改绝对导入又很麻烦,那怎麽办呢?

    预先 import 再执行。

     $ python -m abc.xyz
    

    这样执行就类似先 import abc.xyz 在执行它,这样就可以了。

  2. Circular Import(循环导入)

    什麽是循环导入呢?就是A导入了B,然後B又导入A,这样 Python 会报错。

    如果真的必须这样又有甚麽解决方式呢?可以透过不要导入整个模组,只导入里面的 Class or Function 就可以避免。

参考资料

Python 的 Import 陷阱

那麽就大概这样,这篇是让你在之後较大的专案可以更灵活的 import 而已。

大家掰~掰~


<<:  自动化测试,让你上班拥有一杯咖啡的时间 | Day 9 - 如何上传图片

>>:  Day-08 比训练更重要的事情,Dataset

【Day 03】- 打针!打针!从 R0 注入的那件事!

Agenda 资安宣言 测试环境与工具 学习目标 技术原理与程序码 References 下期预告 ...

[Golang] Go Installation and Basic Toolchain Introduction

Installation Download the package from https://gol...

存取方法

终於度过前面枯燥乏味的内容了...(但它们都很重要,也与今天的主题有关) 今天要来进入重点项目 我们...

JS 原始型别的包裹物件与原型的关联 DAY67

var b = new String('abcdef'); // 这里 String 为建构函式 c...

TailwindCSS 从零开始 - 增加 Base 样式

什麽是 Base 样式 概念有点像是 CSSreset,现在网页基本上都会使用 CSS reset...