Python: 模块化

Python 基础: Module 模块化

簡介

今天来介绍 python 的模块化机制。当软件开发到达一定的规模之后进行模块化是必须的,而在 python 的模块化机制中最关键的就是 __init__.py 文件,相对于直接根据文件名引入方法,提供了屏蔽模块内部结构的特性。

參考

Python 模块-菜鸟教程https://www.runoob.com/python/python-modules.html
__init__.py 提前暴露要调用的方法、模块https://blog.csdn.net/yjinyyzyq/article/details/85317112

正文

Import 引入方法

在一般的 python 编程中,我们常常会引入一些第三方包(package),如 mathnumpy 等,而这些包存放的位置可能是本地的 pip 目录之下,也可能是在各个项目中使用 venv 包建立独立的包依赖虚拟环境,使用方法如下

  • 语法
# 引入指定模块(使用 as 指定别名)
import <module-name> [as <module-alias>]
# 引入指定方法
from <module-name> import <func-name> [as <func-alias>] [, ...<func-name> [as <func-alias>]]
  • 实例
import numpy as np
from math import sin

if __name__ == '__main__':
    a = np.arange(0, 10)
    print(a) 
    # [0 1 2 3 4 5 6 7 8 9]
    
    sin0 = sin(0)
    print(sin0) 
    # 0.0

Import 路径

那这时候可能会有些人困在包路径的问题上。其实 import 语句查找包的路径就两个(可能实际环境会存在多个可选的路径,但是使用 venv 并且建立良好的模块化结构之后,主要管理包的路径就是下面这两个

  1. 相对路径(当前文件所处模块)
  2. 全局环境第三方依赖

自定义模块

现在我们可以在我们的项目中建立一个自定义的模块,结构如下

  • 项目结构
/app
    main.py
    moduleA.py
  • moduleA.py
def funcA():
    print('invoke funcA in moduleA')
  • main.py

两种种引入模式

# 1. 只引入方法
from moduleA import funcA

if __name__ == '__main__':
    funcA() # invoke funcA in moduleA
    
# 2. 引入整个模块
import moduleA

if __name__ == '__main__':
    # 需要加上模块名来找到方法
    moduleA.funcA() # invoke funcA in moduleA

屏蔽模块名:使用 __init__.py

上面透过文件名(文件为单位的模块)来引入模块中的方法,然而在复杂的项目结构中,我们可能希望屏蔽模块内部的文件名和目录结构,我们可以透过加入 __init__.py 文件来使一个目录转换成一个 python 模块

引入一个目录

上面的例子我们将文件视为一个模块来引入,而当我们引入的文件是一个 python 模块的时候,则会执行 __init__.py 文件并导出模块,下面我们看看加载的过程

  • 项目结构
/app
    main.py
    /moduleA
        __init__.py
  • moduleA/__init__.py
print('load moduleA')
  • main.py
import moduleA

if __name__ == '__main__':
    pass

# output:
# load moduleA

当我们使用 main.py 作为主入口时,import moduleA 会加载 moduleA/__init__.py 并执行

将模块中的方法暴露到模块中

现在我们可能在 /moduleA 中写了很多其他的方法和函数,结构如下:

  • 项目结构
/app
    main.py
    /moduleA
        __init__.py
        printer.py
  • moduleA/printer.py
def funcA():
    print('invoke printerA.funcA')

如果我们从模块外部引用时需要知道方法的具体文件和路径会非常的不便,而且会出现异常复杂的 import 路径,严重影响代码可读性,所以我们可以在模块的 __init__.py 将模块内部的方法暴露到目录之下

  • moduleA/__init__.py
# 前面加上 . 指定当前目录下的模块
from .printer import funcA

print('load moduleA')

这时候我们就可以直接从 /moduleA 目录找到并引入 funcA 方法了

  • main.py
from moduleA import funcA

if __name__ == '__main__':
    funcA()

# output:
# load moduleA  ; 引入模块时的输出
# invoke printerA.funcA  ; 调用 funcA 方法的输出

嵌套的模块

嵌套的模块逻辑也是相似的,只要掌握 import 语句查找模块的路径顺序:当前目录 -> 全局依赖,那么不论嵌套多少层都不会搞混,举个例子:

  • 项目结构
/app
    main.py
    /moduleA
        __init__.py
        printer.py
        /sub_module
            __init__.py
            sub_printer.py
  • moduleA/sub_module/sub_printer.py
def subFuncA():
    print('invoke sub_printer.subFuncA')
  • moduleA/sub_module/__init__.py
from .sub_printer import * # 引入 subFuncA 方法

print('load sub_module success')
  • moduleA/printer.py
def funcA():
    print('invoke printerA.funcA')
  • moduleA/__init__.py
from .printer import * # 引入 funcA 方法
from .sub_module import * # 引入 subFuncA 方法

print('load moduleA success')
  • main.py
from moduleA import funcA, subFuncA

if __name__ == '__main__':
    funcA()
    subFuncA()

# output:
# load sub_module success
# load moduleA success
# invoke printerA.funcA
# invoke sub_printer.subFuncA

从输出我们就能够轻易的看出模块引入的顺序(注意 import 使用的路径,都是直接引入目录作为模块而不是指定的文件)

  • 模块执行循序

1. from moduleA import funcA, subFuncA
   引入 moduleA 目录作为模块,执行 moduleA.__init__.py
    1.1 from .printer import *
        引入 printer (文件作为模块)中的所有方法
    1.2 from .sub_module import *
        引入 sub_module 中(目录作为模块)的所有方法,执行 sub_module/__init__.py
        1.2.1 from .sub_printer import *
              引入 sub_printer (文件作为模块)中的所有方法
        1.2.2 print('load sub_module success')
              模块 sub_module(目录作为模块)导出完成并输出
    1.3 print('load moduleA success')
        模块 moduleA(目录作为模块)导出完成并输出
2. funcA() & subFuncA()
   执行从 moduleA 找到的两个方法并输出

結語

以上就是 python 项目中的模块化使用方法,核心思想就是以下两点

  • import 语句查找模块顺序

    1. 当前目录
    2. 全局环境依赖(包)
  • 以目录为模块时加载 __init__.py 的内容

良好的项目结构和模块化设计能够大大降低开发的复杂度和提高项目的可阅读性。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值