模块
模块概念
python程序由模块组成, 一个模块对应一个python源文件, 一般后缀名为.py
模块由语句组成, 运行python程序时, 按照模块中语句的顺序依次执行
语句是python程序的构造单元, 用于创建对象, 变量赋值, 调用函数, 控制语句等
标准库模块
与函数类似, 模块也分为标准库模块和用户自定义模块
Python标准库提供了操作系统功能\网络通信\文本处理\文件处理\数学运算等基本功能, 比如: random(随机数), math(数学运算), time(时间处理), file(文件处理), os(和操作系统交互), sys(与解释器交互)等
另外Python还提供了海量的第三方模块, 使用方式和标准库类似, 功能覆盖了诸多领域, 比如: 科学计算, WEB开发, 大数据, 人工智能, 图形系统等
为什么需要模块化编程
模块(module)对应于Python源代码文件, 模块中可以定义变量\函数\类\普通语句, 这样, 我们可以将一个Python程序分解成多个模块, 便于后期的重复利用
模块化编程的优点:
- 便于将一个任务分解成多个模块, 实现团队协调开发, 完成大规模程序
- 实现代码复用, 一个模块实现后, 可以被反复利用
- 可维护性增强
模块化编程的流程
- 设计API, 进行功能描述
- 编码实现API中描述的功能
- 在模块中编写测试代码, 并消除全局代码
- 使用私有函数实现不被外部客户端调用的模块函数
API描述
API(Application Programming Interface 应用程序编程接口)是用于描述模块中提供的函数和类的功能描述和使用方式描述
模块化编程中, 首先设计的是模块的API(即要实现的功能描述), 然后开始编码实现API中描述的功能, 最后在其他模块中导入本模块进行调用
可以通过help(模块名)查看模块的API, 一般使用时先导入模块, 然后通过help函数查看
模块文档字符串
我们可以在模块的第一行增加一个文档字符串, 用于描述模块的相关功能
可以通过__doc__可以获得模块的文档字符串的内容
测试代码
每个模块都有一个名称, 通过特殊变量__name__可以获取模块的名称, 在正常情况下, 模块名称对应源文件名称
仅有一个例外, 就是当一个模块被作为程序入口时(主程序\交互式提示符下), 它的__name__的值为__main__
因此, 我们可以根据这个特点, 将模块源代码中的测试代码进行独立的处理
if __name__ = "__main__":
# 测试代码
模块的导入
模块化设计的好处之一就是"代码复用性高", 写好的模块可以被反复调用
模块的导入就是"在本模块中使用其他模块"
import语句导入
import module
import module1, module2
import module as new_name
import加载的模块分为四种类型:
- 使用python编写的代码.py文件
- 已被编译为共享库或DLL的C或C++扩展
- 一组模块的包
- 使用C编写并链接到python解释器的内置模块
我们一般通过import语句实现模块的导入和使用, import本质上是使用了内置函数__import__()
当我们通过import导入一个模块时, python解释器进行执行, 最终会生成一个module类的对象, 这个对象就代表了被加载的模块
from...import导入
基本语法如下:
from module import member1, member2 # 导入模块的多个成员
from module import * # 导入模块所有成员
注意: 尽量避免from module import *这种写法, *表示导入模块中所有非私有的属性\方法, 但你不知道导入的所有内容, 很有可能会覆盖之前定义好的名字, 而且代码的可读性较差, 一般生产环境中尽量避免使用
import语句和from...import语句的区别
import导入的是模块, from...import导入的是模块的函数\类
__import__()动态导入
import语句本质上就是调用内置函数__import__(), 我们可以通过它实现动态导入, 给__import__()动态传递不同的参数值, 就能导入不同的模块
s = "math"
m = __import__(s) # 导入后生成的模块对象的引用给变量m
print(m.pi)
注意: 一般不建议我们自行使用__import__()导入, 其行为在python2和python3中有差异, 会导致意外错误, 如果需要动态导入可以使用importlib模块
import importlib
a = importlib.import_module("math")
print(a.pi)
模块的加载问题
当导入一个模块时, 模块中的代码都会执行, 不过, 如果再次导入这个模块, 则不会再次执行
Python的设计者为什么这么设计? 因为, 导入模块更多的时候需要的是定义模块中的变量\函数\对象等, 这些不需要反复定义和执行, 只需要导入一次就足够了
一个模块无论导入多少次, 这个模块在整个解释器进程内有且仅有一个实例对象
重新加载
有时候我们确实需要重新加载一个模块, 这时候可以使用: importlib.reload()方法
import module
import importlib
importlib.reload(module)
包
包的概念和结构
当一个项目中有很多个模块时, 需要再进行组织, 我们将功能类似的模块放到一起, 形成了"包"
本质上, "包"就是一个必须有__init__.py的文件夹
包下面可以包含"模块(module)", 也可以包含"子包(subpackage)"
导入包操作和本质
操作如下:
import package.subpackage.module
from package.subpackage import module
from package.subpackage.module import function
导入包的本质其实就是导入包的__init__.py文件, 也就是说, import package意味着执行了包下面的__init__.py文件, 这样, 可以在__init__.py中批量导入我们需要的模块, 而不需要逐一导入
__init__.py的核心作用:
- 作为包的标识, 不能删除
- 导入包实质就是执行__init__.py文件, 可以在__init__.py文件中做关于包的初始化, 以及需要统一执行代码, 或者批量导入
包的模糊导入
用*导入包
import * 这样的语句理论上是希望文件系统找出包中所有的子模块, 然后导入所有模块, 这样可能花费比较多的时间, 因此Python的解决方案是提供一个明确的包索引
这个索引由__init__.py定义的__all__变量, 该变量为一个列表
举个例子: 在package包下的__init__.py定义的__all__=["module1", "module2"]
这就意味着, from package import * 会从对应的包中导入以上两个子模块
sys.path和模块搜索路径
当我们导入某个模块文件时, Python解释器会去哪里找这个文件呢? 只有找到这个文件才能读取\加载运行该模块文件, 因此它一般按照以下路径寻找模块文件的(按照顺序寻找, 找到即停止不再继续寻找)
- 内置函数
- 当前目录
- 程序的主目录
库
PIP模块管理工具
pip是一个现代的, 通用的Python包管理工具, 提供了对Python包的查找, 下载, 安装, 卸载的功能
pip更换国内源
家目录中, 创建pip目录, 增加文件: pip.ini, 拷贝内容如下:
[global]
index-url=https://mirrors.aliyun.com/pypi/simple/
[install]
trusted-host=mirrors.aliyun.com
Linux的家目录为: ~, 增加目录和文件: ~/pip/pip.ini
Windows的家目录为: C:/user/用户名, 增加目录和文件: C:/user/用户名/pip/pip.ini
常见的国内源:
https://mirrors.aliyun.com/pypi/simple 阿里云
https://pypi.douban.com/simple 豆瓣
https://pypi.tuna.tsinghua.edu.cn/simple 清华大学
https://pypi.hustunique.com/ 华中科技大学
https://pypi.mirrors.ustc.edu.cn/simple 中国科技大学
https://pypi.sdutlinux.org/ 山东理工大学
Pycharm进行pip换源: 新版PyCharm进行pip换源_pycharm切换源_一唸辶间的博客-CSDN博客