Python学习日记-5

126 阅读7分钟

模块

模块概念

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解释器会去哪里找这个文件呢? 只有找到这个文件才能读取\加载运行该模块文件, 因此它一般按照以下路径寻找模块文件的(按照顺序寻找, 找到即停止不再继续寻找)

  1. 内置函数
  2. 当前目录
  3. 程序的主目录

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博客