文章目录
一、模块化程序设计理念
1. 语句、函数、模块、包
语句,函数,类 – 文件中的内容 (函数和变量组成了类)
模块 – 文件
包 – 文件夹
2.模块化编程
(1). 为什么需要模块化编程
- 模块化编程(Modular Programming)将一个任务分解成多个模块。每个模块就像一个积木一样,便于后期的反复使用、反复搭建。
- 模块化编程有如下几个重要优势:
(1). 便于将一个任务分解成多个模块,实现团队协同开发,完成大规模程序
(2). 实现代码复用。一个模块实现后,可以被反复调用。
(3). 可维护性增强。
(2). 模块化编程的一般流程:
- 设计 APII(Application Programming Interface 应用程序编程接口),进行功能描述。
- 编码实现 API 中描述的功能。
- 在模块中编写测试代码,并消除全局代码。
- 使用私有函数实现不被外部客户端调用的模块函数。
(3). 模块的 API 和功能描述要点
- API(Application Programming Interface 应用程序编程接口)是用于描述模 块中提供的函数和类的功能描述和使用方式描述。
- 模块化编程中,首先设计的就是模块的 API(即要实现的功能描述),然后开始编 码实现 API 中描述的功能。最后,在其他模块中导入本模块进行调用。
- 我们可以通过help(模块名)查看模块的API。一般使用时先导入模块 然后通过help函数查看。
#导入 math 模块,并通过 help()查看 math 模块的 API:
import math
help(math)
运行结果:
出现math函数的API(应用程序编程接口),就是math里头有啥就出来啥
(4).设计计算薪水模块的API
#模块名是Salary
'''
本模块用于计算薪水
'''
company = '拜师教育'
def yearSalary(monthSalary):
'''计算年薪:monthSalary*12'''
return monthSalary*12
def daySalary(monthSalary):
'''计算日薪:monthSalary/22.5'''
return monthSalary/22.5
if __name__=='__main__': #如果名字就是main 代表着这里是模块本身,不是其他
print(yearSalary(22500))
print(daySalary(22500))
运行结果:
270000
1000.0
import Salary
print(Salary.__name__)
print(Salary.__doc__) #通过 __doc__可以获得文档字符串的内容
print(Salary.yearSalary.__doc__)
运行结果:
Salary 运行结果是Salary说明这是调用的不是模块本省
本模块用于计算薪水
计算年薪:monthSalary*12
二、导入模块
- 模块化设计的好处之一就是“代码复用性高”。写好的模块可以被反复调用,重复使用。 模块的导入就是“在本模块中使用其他模块”。
1. import语句导入
-
import语句的基本语法格式如下:
(1)import 模块名 #导入一个模块
(2)import 模块 1,模块 2… #导入多个模块
(3)import 模块名 as 模块别名 #导入模块并使用新名字 -
import 加载的模块分为四个通用类别:
a.使用 python 编写的代码(.py 文件);
b.已被编译为共享库或 DLL 的 C 或 C++扩展;
c.包好一组模块的包
d.使用 C 编写并链接到 python 解释器的内置模块; -
我们一般通过 import 语句实现模块的导入和使用,import 本质上是使用了内置函数 import()。
-
当我们通过 import 导入一个模块时,python 解释器进行执行,最终会生成一个对象, 这个对象就代表了被加载的模块。
import math
print(id(math))
print(type(math))
print(math.pi) #通过math.成员名 来访问模块中的成员
运行结果:
2293437643328
<class 'module'>
3.141592653589793
- 由上,我们可以看到 math 模块被加载后,实际会生成一个 module 类的对象,该对象被 math 变量引用。
- 我们可以通过 math 变量引用模块中所有的内容。 我们通过 import 导入多个模块,本质上也是生成多个 module 类的对象而已。
- 有时候,我们也需要给模块起个别名,本质上,这个别名仅仅是新创建一个变量引用加 载的模块对象而已。
#import math as m #与
import math
m=math #一样
print(m.sqrt(9))
运行结果:
3.0
2.from…import导入
- python 中可以使用 from…import 导入模块中的成员。基本语法格式如下:
from 模块名 import 成员1,成员2,… - 如果希望导入一个模块中的所有成员,则可以采用如下方式:
from 模块名 import *
【注】尽量避免“from 模块名 import ”这种写法。 它表示导入模块中所有的不 是以下划线(_)开头的名字都导入到当前位置。 但你不知道你导入什么名字,很有可能 会覆盖掉你之前已经定义的名字。而且可读性极其的差。一般生产环境中尽量避免使用。
#使用 from…import 导入模块指定的成员
from math import pi,sin
print(sin(pi/2))
运行结果:
1.0
3. import 语句和 from…import 语句的区别
- import 导入的是模块。from…import 导入的是模块中的一个函数/一个类。
我们自定义一个模块 calculator.py:
'''
一个实现四则运算的计算器
'''
def add(a,b):
return a+b
def minus(a,b):
return a-b
我们在另一个模块 test.py 测试:
import calculator
a=calculator.add(30,40) #不加模块名无法识别
print(a)
from calculator import *
a=add(30,40) #无需模块名,可以直接引用里面的函数/类
print(a)
运行结果:
70
4. import()动态导入
- import 语句本质上就是调用内置函数__import__(),我们可以通过它实现动态导入。给 import()动态传递不同的的参数值,就能导入不同的模块。
#使用__import__()动态导入指定的模块
t='math'
m=__import__(t)
print(m.pi)
运行结果:
3.141592653589793
- 一般不建议我们自行使用__import__()导入,其行为在 python2 和 python3 中 有差异,会导致意外错误。如果需要动态导入可以使用 importlib 模块。
import importlib
a=importlib.import_module('math')
print(a.pi)
运行结果:
3.141592653589793
5. 模块的加载问题
- 当导入一个模块时, 模块中的代码都会被执行。不过,如果再次导入这个模块, 则不会再次执行。
- 一个模块无论导入多少次,这个模块在整个解释器进程内有且仅有一个实例对象。
o1.py的源代码
print('o1被加载了')
o2.py的源代码
import o1 #会执行 test02 模块中的语句 #
import o1 #不会再执行 test02 模块中的语句
重新加载
有时候我们确实需要重新加载一个模块,这时候可以使用:importlib.reload() 方法
import o1
import o1
print('***********')
import importlib
importlib.reload(o1)
运行结果:
o1被加载了
***********
o1被加载了
三、包package的使用
1. 导入包操作和本质
- 以module_AA.py为例,导入代码如下:
- import a.aa.module_AA 在使用时,必须加完整名称来引用,比如:a.aa.module_AA.fun_AA()
- from a.aa import module_AA 在使用时,直接可以使用模块名。 比如:module_AA.fun_AA()
- from a.aa.module_AA import fun_AA 直接导入函数 在使用时,直接可以使用函数名。 比如:fun_AA()
-
【注】
(1). from package import item 这种语法中,item 可以是包、模块,也可以是函数、 类、变量。
(2). import item1.item2 这种语法中,item 必须是包或模块,不能是其他。 -
导入包的本质其实是“导入了包的__init__.py”文件。也就是说,”import pack1”意味 着执行了包 pack1 下面的__init__.py 文件。 这样,可以在__init__.py 中批量导入我们需要 的模块,而不再需要一个个导入。
-
init.py 的三个核心作用:
(1). 作为包的标识,不能删除。
(2). 用来实现模糊导入
(3). 导入包实质是执行__init__.py 文件,可以在__init__.py 文件中做这个包的初始化、以及 需要统一执行代码、批量导入。 -
[示例]测试包的__init__.py 文件本质用法
a 包下的__init__.py 文件内容:
import turtle
import math
print('导入a包')
b 包下的 module_B1.py 文件中导入 a 包,代码如下:
import a
print(a.math.pi)
运行结果:
导入a包
3.141592653589793
【注】如上测试我们可以看出 python 的设计者非常巧妙的通过__init__.py 文件将包转成了 模块的操作。因此,可以说“包的本质还是模块”。
2. 用 * 导入包
- import * 这样的语句理论上是希望文件系统找出包中所有的子模块,然后导入它们。 这可能会花长时间等。Python 解决方案是提供一个明确的包索引。
- 这个索引由 _ init _.py 定义 _ all _ 变量,该变量为一列表,如上例 a 包下的 _ init _.py 中,可定义 _ all _ = [“module_A”,“module_A2”]
- 这意味着, from sound.effects import * 会从对应的包中导入以上两个子模块;
【注】尽管提供 import * 的方法,仍不建议在生产代码中使用这种写法。
3. 包内引用
- 如果是子包内的引用,可以按相对位置引入子模块 以 aa 包下的 module_AA 中导入 a 包下内容为例:
from … import module_A #…表示上级目录 .表示同级目录
from . import module_A2 #.表示同级目录