1 关于模块化编程
1.1 引言
之前有介绍过函数,函数是为了来封装公共的功能,当然也存在有大量公共的函数和变量,我们可以使用类和对象来对函数和变量进行统一的管理和调用。但是随着程序变得复杂,函数和类更加多了,我们可以怎么办呢?这时候“模块”就诞生了。模块一多,“包”就出现了。
包 > 模块 > 类和对象 > 函数
模块对应于python源代码文件。模块中可以定义变量、函数、类、普通语句。模块化编程将一个任务分解成多个模块。每个模块就像一个积木一样,便于后期的反复使用、反复搭建。
模块化编程的好处:
- 合理控制模块规模:按照逻辑性将代码尽可能的划分成多个子模块,方便客户端程序调用。将一个任务分解成多个模块,实现团队协同开发。
- 提高代码的可维护性:从代码的视角分析,变量越多,维护的难度越大。
- 提高代码可重用性:充分利用系统标准库或者自己编写的第三方库来为程序服务。
1.2 模块化编程流程
- 设计API,进行功能描述
- 编码实现API中描述的功能
- 在模块中编写测试代码,并消除全局代码
- 使用私有函数实现不被外部客户端调用的模块函数
API(Application Programming Interface),API没有规定具体的代码实现,只是提供访问模块的规范。API的设计应该包括多少信息,也没有强制的规定。一般包含函数名称、参数类型、返回类型等。
如以下可以查看math
库的api文档
import math
help(math)
这里节选一部分
Help on built-in module math:
NAME
math
DESCRIPTION
This module provides access to the mathematical functions
defined by the C standard.
FUNCTIONS
sin(x, /)
Return the sine of x (measured in radians).
sinh(x, /)
Return the hyperbolic sine of x.
sqrt(x, /)
Return the square root of x.
DATA
e = 2.718281828459045
inf = inf
nan = nan
pi = 3.141592653589793
tau = 6.283185307179586
FILE
(built-in)
当然,我们在python安装目录的docs目录下,也可以看到python的api文档(不确定使用conda方式安装的python下是否有)
1.3 模块的创建
- 功能描述与实现
# student.py
"""本模块用于计算学生的成绩"""
def getTotalScore(scores):
"""计算总分"""
return sum(scores)
def getAvageScore(scores):
"""计算平均分"""
return sum(scores) / len(scores)
- 模块的导入与使用
静态导入
import xxx
如import math
from xxx import xx
如from math import sqrt
,from math import *
# main.py
import student # 注意 只有在这一个
print(student.__doc__) # 本模块用于计算学生的成绩
print(student.getTotalScore.__doc__) # 计算总分
print(student.getAvageScore.__doc__) # 计算平均分
from student import getAvageScore
print(getAvageScore([90, 80 ,70, 60]))
动态导入
使用 __import__
函数导入
s = 'student'
m = __import__(s) #导入后生成的模块对象的引用给变量 m
print(m.getTotalScore.__doc__)
一般不建议使用__import__
函数的方式引入,可以使用importlib库
import importlib
a = importlib.import_module('math')
print(a.pi)
导入一个模块的时候,模块中的代码就会被执行。如果再次导入这个模块,则模块中的代码不会被再次执行。Python这么设计的原因:模块更多的是定义变量、函数、对象等,这些并不需要反复定义和执行。
import student
print(dir(student)) # ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'getAvageScore', 'getTotalScore']
print(student.__name__) # __name__ 内置属性
2 包
当一个项目中有很多的模块的时候,我们需要对模块进行组织。因此就有了包的概念,本质上『包』就是一个必须包含了__init__.py
的文件夹。一般结构如下:
包中可以包含模块,也可以包含子包
├── a/
│ ├── __init__.py
│ └── a_module.py
├── b/
│ ├── __init__.py
│ └── b_module.py
│ ├── bb/
│ │ └── bb_module.py
2.1 包的引入
具体代码目录结构与代码
__init__.py
文件中可以不写任何代码,但是必须存在,它定义了包的属性和方法,在导入模块之前,一定会先执行__init__.py
中的内容,如果没有__init__.py
文件,那么目录就仅仅是一个目录,而不是一个包。__pycache__
为python缓存文件
a模块中的__init__.py
文件
print("这是a模块中的__init__.py文件")
import a.a_module
import b.b_module
import b.bb.bb_module
from a import a_module
a_module.a_function()
from a.a_module import a_function
a_function()
# 输出
# 这是a模块中的__init__.py文件
# a_module
# b_module
# bb_module
# a function
# a function
2.2 sys.path 和模块搜索路径
当导入某一个模块文件的时候,python解释器一般会按照一下的路径搜索模块文件L:
- 内置模块
- 当前目录
- 程序的主目录
- pythonpath目录(如果已经设置了pythonpath的环境变量)
- 标准链接库目录
- 第三方库目录(site-packages目录)
.pth
文件的内容(如果存在)sys.path.append()
临时添加的目录
当任何一个python程序启动的时候,就将上面的搜索路径(除内置模块以外的路径)进行收集,放在sys
模块的path属性中。如下
/Users/maplewan/Desktop/AI-Go
为当前目录,我们通过sys.path.append("./")
将当前目录放入了sys.path
中
import sys
sys.path.append("./")
print(sys.path)
# 输出
'''
['/Users/maplewan/Desktop/AI-Go', '/Users/maplewan/anaconda3/lib/python311.zip', '/Users/maplewan/anaconda3/lib/python3.11', '/Users/maplewan/anaconda3/lib/python3.11/lib-dynload', '/Users/maplewan/anaconda3/lib/python3.11/site-packages', '/Users/maplewan/anaconda3/lib/python3.11/site-packages/aeosa', './']
'''
2.3 模块的发布和安装
模块开发完成之后,我们可以对外开放,其他开发者也可以以『第三方扩展库』的方式来使用我们的模块
对于以上的代码,我们可以在项目目录下面新建一个 setup.py
文件,如下
from distutils.core import setup
setup(
name = 'mapleTest',
version= '1.0',
description= 'maple测试',
author= 'maple',
author_email= '1041709112@qq.com',
py_modules= ['b.b_module', 'b.bb.bb_module'],
)
然后执行 python setup.py sdist
,根目录下会出现一个dist
文件夹,其中有mapleTest-1.0.tar.gz
压缩包,我们可以使用python setup.py install
来将模块安装,我们可以在python安装目录lib/site-pacakages
目录下发现mapleTest
文件夹,说明安装成功。这种install的方式在最新的python版本中已经被废弃。报错如下
/Users/maplewan/anaconda3/lib/python3.11/site-packages/setuptools/_distutils/cmd.py:66: SetuptoolsDeprecationWarning: setup.py install is deprecated.
!!
********************************************************************************
Please avoid running ``setup.py`` directly.
Instead, use pypa/build, pypa/installer or other
standards-based tools.
See https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html for details.
********************************************************************************
!!
self.initialize_options()
/Users/maplewan/anaconda3/lib/python3.11/site-packages/setuptools/_distutils/cmd.py:66: EasyInstallDeprecationWarning: easy_install command is deprecated.
!!
********************************************************************************
Please avoid running ``setup.py`` and ``easy_install``.
Instead, use pypa/build, pypa/installer or other
standards-based tools.
See https://github.com/pypa/setuptools/issues/917 for details.
********************************************************************************
!!
self.initialize_options()
zip_safe flag not set; analyzing archive contents...
关于模块发布到PyPI 网站,可参考 https://zhuanlan.zhihu.com/p/682004873 (我也没做过这个)
3 库
python中米有对库具体的定义。模块和包侧重于代码的组织,有明确的定义。一般情况下,库强调的是功能性,而不是代码的组织。通常将某个功能的『模块的集合』,称为库
- 标准库:提供系统管理、网络通信、文本处理、数据库接口、图形系统、XML处理等额外的功能,如
random, math, os
等 - 三方扩展库:
pip, jieba, flask, pytorch
等
我们可以通过 pip install XX
来安装库
如安装flask, pip install flask