Python小课(7)使用库
库:就是是别人已经写好了的代码,可以让我们直接拿来用。

一个编程语言能不能流行起来,一方面取决于语法是否简单方便容易学习,一方面取决于生态是否完备。

实际开发中,也并非所有的代码都自己手写,而是要充分利用现成的库,简化开发过程。
按照库的来源,可以大致分成两大类:
- 标准库:Python 自带的库。只要安装了 Python 就可以直接使用。
- 第三方库:其他人实现的库。要想使用,需要先进行安装。
咱们自己也可以实现 "第三方库" 发布出去,交给别人来使用。

1. 标准库
1.1 认识标准库
Python 自身内置了非常丰富的库。
在 Python 官方文档上可以看到这些库的内容。
https://docs.python.org/3.10/library/index.html


简单来说,主要是这些部分:
- 内置函数(如 print、input、……等)
- 内置类型(针对 int, str, bool, list, dict 等类型内置的操作)
- 文本处理
- 时间日期
- 数学计算
- 文件目录
- 数据存储(操作数据库、数据序列化等)
- 加密解密
- 操作系统相关
- 并发编程相关(多进程, 多线程, 协程, 异步等)
- 网络编程相关
- 多媒体相关(音频处理、视频处理等)
- 图形化界面相关
- .......
我们不需要把这些库的内容都背下来,只要大概知道里面有啥,需要用的时候能够找到即可。
1.2 使用 import 导入模块
使用 import 可以导入标准库的一个 模块。
import [模块名]

1.3 【代码示例】日期计算
【要求】输入任意的两个日期,计算两个日期之间隔了多少天。
- 使用 import 语句导入标准库的 datetime 模块。(功能:完成日期相关的计算)
- 使用 datetime.datetime 构造两个日期,参数使用 年, 月, 日 这样的格式。
- 两个日期对象相减,即可得到日期的差值。
import datetime
date1 = datetime.datetime(2012, 2, 14)
date2 = datetime.datetime(2022, 7, 12)
print(date2 - date1)

除了查看官方文档,还可以查看本地的离线文档。


【改进方法1】导入类型名

从datetime模块中导入datetime这个类型。(包括里面的数据、方法)
【改进方法2】起别名

关于 datetime 的更多操作,可以参考官方文档
https://docs.python.org/3/library/datetime.html
1.4 【代码示例】字符串操作
字符串是 Python 的内置类型,字符串的很多方法不需要导入额外的模块,即可直接使用。
(1)剑指offer 58-翻转单词顺序
https://leetcode.cn/problems/fan-zhuan-dan-ci-shun-xu-lcof/


单词序逆序,单词内部的字母序不变。

【步骤】
- 使用 str 的 split 方法进行字符串切分,指定 空格 为分隔符,返回结果是一个列表。
- 使用 list 的 reverse 方法进行逆序。
- 使用 str 的 join 方法进行字符串拼接,把列表中的内容进行合并,在合并的时候使用空格作为分隔符。
def reverseWords(s):
tokens = s.split()
tokens.reverse()
return ' '.join(tokens)
print(reverseWords('I am a student.'))

(2)leetcode 796-旋转字符串
https://leetcode.cn/problems/rotate-string/



【步骤】
- 使用 len 求字符串的长度,如果长度不相同,则一定不能旋转得到。
- 将 s 和 自己 进行拼接,然后直接使用 in 方法来判定 goal 是否是 s + s 的子串。
def rotateString(s, goal):
return len(s) == len(goal) and goal in s + s
print(rotateString('abcde', 'cdeab'))

(3)leetcode 2255-统计是给定字符串前缀的字符串数目
https://leetcode.cn/problems/count-prefixes-of-a-given-string/


【步骤】
- 依次遍历 words 中的字符串。
- 直接使用字符串的 startswith 方法即可判定当前字符串是否是 s 的前缀。
def countPrefixes(words, s):
res = 0 # 符合要求字符串个数
for word in words:
if s.startswith(word):
res += 1
return res
print(countPrefixes(["a","b","c","ab","bc","abc"], "abc"))

关于字符串的更多操作,参考官方文档。
https://docs.python.org/3/library/stdtypes.html#str
1.5 【代码示例】文件查找工具
【要求】指定一个待搜索路径,同时指定一个待搜索的关键字。在待搜索路径中查找是否文件名中包含这个关键字。
【步骤】
- 导入 os 模块。
- 使用 os.walk 即可实现目录的递归遍历.
- os.walk 返回一个三元组,分别是 当前路径 ,当前路径下包含的目录名(多个),当前路径下包含的文件名(多个)

# 实现文件查找工具
# 输入要查找的路径, 输入要搜索的文件名(一部分)
# 自动的在指定的路径中进行查找~
import os
inputPath = input('请输入要搜索的路径:')
pattern = input('请输入要搜索的关键词:')
for dirpath, dirnames, filenames in os.walk(inputPath):
print('---------------------------------')
print(f'dirpath = {dirpath}')
print(f'dirnames')
for name in dirnames:
print(name)
print(f'filenames')
for name in filenames:
print(name)
【检验os.walk的功能】
正斜杠、反斜杠都可以。大写D、小写d都可以。


【题解】
import os
inputPath = input('请输入待搜索路径: ')
pattern = input('请输入待搜索关键词: ')
for dirpath, dirnames, filenames in os.walk(inputPath):
for f in filenames:
if pattern in f:
print(f'{dirpath}/{f}')

其中dirnames用不上,可以使用_占位。
import os
inputPath = input('请输入待搜索路径: ')
pattern = input('请输入待搜索关键词: ')
for dirpath, _, filenames in os.walk(inputPath):
for f in filenames:
if pattern in f:
print(f'{dirpath}/{f}')
【说明】这里只是简单粗暴的遍历查找,效率不高,提高效率可以寻求一些高效的数据结构。
关于 os 模块的更多操作, 参考官方文档。
https://docs.python.org/3/library/os.html
2. 第三方库
2.1 认识第三方库
第三方库就是别人已经实现好了的库,我们可以拿过来直接使用。
虽然标准库已经很强大了,但是终究是有限的。而第三方库可以视为是集合了全世界 Python 程序猿的智慧,可以说是几乎无穷无尽。
问题来了,当我们遇到一个需求场景的时候,如何知道,该使用哪个第三方库呢?
就需要用到下面几个网站了:

当我们确定了该使用哪个第三方库之后,就可以使用 pip 来安装第三方库了。
【步骤】
- 查找目标第三方库;
- 下载目标第三方库;
2.2 使用 pip
pip 是 Python 内置的 包管理器。
pip 在我们安装 Python 的时候就已经内置了,无需额外安装。
pip 是一个可执行程序,就在 Python 的安装目录中。
pip 是一个命令行程序,通过命令行命令调用。



打开 cmd,直接输入 pip,如果显示以下帮助信息,说明 pip 已经准备就绪。

如果最开始按照要求在安装 Python 的时候勾选了:

那么 pip 就是默认可用的。
如果提示
'pip' 不是内部或外部命令,也不是可运行的程序 或 批处理文件。
则说明没有正确的把 pip 加入到 PATH 中,可以手动把 pip 所在的路径加入到 PATH 环境变量中。(参考 https://www.jianshu.com/p/1de0acf7185d)
或者卸载重装 Python,记得勾上上述选项,也许是更简单的办法。
在命令行(cmd命令行,或者pycharm内部集成的命令行)。
使用以下命令,即可安装第三方库。
pip install [库名]
【注意】这个命令需要从网络上下载,使用时要保证网络畅通。
安装成功后,即可使用 import 导入相关模块,即可进行使用。
【注意】如果使用 pip 安装完第三方库之后,在 PyCharm 中仍然提示找不到对应的模块,则检查 Settings -> Project -> Python Interpreter ,看当前 Python 解释器设置的是否正确。
如果一个机器上安装了多个版本的 Python,容易出现这种情况,pip可能把第三方库安装到其他的python版本了。


2.3 【代码示例】生成二维码
(1)通过搜索引擎,确定使用哪个库

得到情报,qrcode 这个库,可以用来生成二维码。

(2)查看 qrcode 文档
在 PyPI 上搜索 qrcode

点击则进入 qrcode 的详情页。
文档开头描述了如何安装 qrcode。

页面中央位置描述了 qrcode 库的使用方法。

(3)使用 pip 安装
pip install qrcode[pil]


警告:22.0版本低,有22.2的版本。



(4)编写代码
按照文档给出的示例,模仿一段代码。
import qrcode
img = qrcode.make('hello_world')
img.save('qrcode.png')




运行完毕后,得到结果如下:

在代码所在目录下,找到qrcode.png:

使用微信或者其他工具扫码,即可看到二维码内容。

纸质资料提供链接,没法复制粘贴,往往就会采用提供二维码的形式,手机扫一扫就能打开。
2.4 【代码示例】操作 excel
读取 excel 可以使用 xlrd 模块。文档地址: https://xlrd.readthedocs.io/en/latest/
修改 excel 可以使用 xlwt 模块。文档地址: https://xlwt.readthedocs.io/en/latest/
此处以 xlrd 为例,演示 excel 的基本操作。
【需求】有如下 excel 表格 d:/test.xlsx

求 100 班的同学的平均分。

(1)安装 xlrd
pip install xlrd==1.2.0(旧版本)

【注意】此处要指定版本号安装。
如果不指定版本号,会安装最新版,而最新版里删除了对 xlsx 格式文件的支持。
(2)编写代码
- 使用 open_workbook 方法打开一个 excel 文件。
- 使用 xlsx.sheet_by_index(0) 获取到 0 号标签页。
- 使用 table.nrows 获取到表格的行数。
- 使用 table.cell_value(row, col) 获取到表格中 row, col 位置的元素值。
import xlrd
# 1. 打开 xlsx 文件
xlsx = xlrd.open_workbook('d:/test.xlsx')
# 2. 获取 0 号标签页. (当前只有一个标签页)
table = xlsx.sheet_by_index(0)
# 3. 获取总行数
nrows = table.nrows # 8行
# 4. 遍历数据
count = 0
total = 0
# 第0行是表头,从第1行数据开始遍历
for i in range(1, nrows):
# 使用 cell_value(row, col) 获取到指定坐标单元格的值——拿到当前同学的班级号
classId = table.cell_value(i, 1) # i行1列
if classId == 100:
total += table.cell_value(i, 2) # i行2列
count += 1
print(f'平均分: {total / count}')

2.5 【代码示例】程序猿鼓励师
【要求】监听键盘按键,每按键 20 下,就自动播放一个音频,鼓励一下辛苦搬砖的自己。
【两个锚点】

不是每隔固定周期发声,而是每按按键多少次——即在高强度敲键盘的时候,再发声。
(1)安装第三方依赖
【说明】
- pynput 用于监听键盘按键,注意版本不要用最新。
- playsound 用于播放音频。
【操作】
pip install pynput==1.6.8 (非最新版)
pip install playsound==1.2.2 (非最新版)


(2)准备音频文件
此处准备了一个 music.mp3、music2.mp3、music3.mp3 放到和 py 代码同级目录中。
音频、视频、图片、……这样的一些文件,称为程序的资源文件。
资源文件是在写一些程序的时候,必要的一些内容。
很多时候所写的代码逻辑,就是把一些资源文件整合起来。
比如:游戏程序——动作模组、音效、背景图、……,都是通过资源文件来描述的。
(3)编写代码
- 使用 from import 的格式直接导入模块中的指定对象/函数.
- 使用 keyboard.Listener 监听键盘按键,其中 on_release 会在释放按键时被调用。
- 使用 listener.start 启动监听器,为了防止程序直接退出,使用 listener.join 让程序等待用户按键。
- 使用 count 计数,每隔 10 次, 调用 playsound 播放音频文件。
首先完成针对键盘的操作:


当我们创建好 Listener 之后,用户的键盘按键动作就会被捕获到。
还希望在捕获到之后能够执行一段代码,可以使用回调函数。


param和return这两个提示词是在文档字符串""" """中间按回车,自动生成的。
当前程序的执行结果:

统计按键次数,然后播放音频。
import random
from pynput import keyboard # 导入键盘模块
from playsound import playsound
soundList = ['sound/music1.mp4','sound/music2.mp4','sound/music3.mp4']
# 记录当前用户按了多少次键盘
count = 0
def onRelease(key):
"""
这个函数, 就是在用户释放键盘按键的时候, 就会被调用到.
这个函数不是咱们自己主动调用的, 而是把这个函数交给了 Listener ,
由 Listener 在用户释放按键的时候自动调用.
像这样的不是咱们自己主动调用, 而是交给别人, 在合适的时机进行调用, 这样的函数, 叫做 "回调函数" (callback function)
:param key: 用户按下了哪个键
:return:
"""
print(key)
global count
count += 1
if count % 20 == 0: # 每敲20次代码,就播放一次音频(频率可以控制)
# 播放音频!
# 生成随机数
i = random.randint(0, len(soundList) - 1)
# 此处的播放音频, 消耗时间比较多, 可能会引起输入的卡顿(不流畅)
# 可以创建一个线程, 在线程里播放音频!!
playsound(soundList[i])
# 创建一个监听器对象,监听键盘的行为
listener = keyboard.Listener(on_release=onRelease)
print(type(listener))
# 启动监听器,但它运行在后台线程中。
listener.start() # 代码不会卡在这里,会立即继续往下执行
# 让主线程等待监听器线程结束。
listener.join() # 如果没有这行,主线程执行完所有代码后会直接退出,导致后台的监听器也被迫关闭。
- 按多少次播放音频可以调整
- 音频的数量可以调整











把.mp3换成.wav(不能直接改后缀,要使用专业转换工具)。
soundList = ['sound/music1.wav','sound/music2.wav','sound/music3.wav']
playsound.py不用改,就最原始的就可以。

换成1~2s的短音频:

此处的播放音频,消耗时间比较多,可能会引起输入的卡顿(不流畅)。
要想更优化地处理程序,可以创建一个线程,在线程里播放音频。
(4)改进代码
上述代码在执行过程中,会感觉到播放音频会导致按键卡顿,可以使用多线程解决这个问题。
关于多线程的知识,在此处不详细介绍。
- 使用 threading.Thread 引入多线程类。
- 使用 Thread 的构造函数来构造一个线程。target 表示线程要执行的任务,args 表示 target 中要调用函数的参数。
- 使用 Thread.start() 启动线程。
import random
from pynput import keyboard # 导入键盘模块
from playsound import playsound
from threading import Thread
# soundList = ['sound/music1.wav','sound/music2.wav','sound/music3.wav']
soundList = ['sound/error.wav','sound/warning.wav']
# 记录当前用户按了多少次键盘
count = 0
def onRelease(key):
"""
这个函数, 就是在用户释放键盘按键的时候, 就会被调用到.
这个函数不是咱们自己主动调用的, 而是把这个函数交给了 Listener ,
由 Listener 在用户释放按键的时候自动调用.
像这样的不是咱们自己主动调用, 而是交给别人, 在合适的时机进行调用, 这样的函数, 叫做 "回调函数" (callback function)
:param key: 用户按下了哪个键
:return:
"""
print(key)
global count
count += 1
if count % 20 == 0: # 每敲20次代码,就播放一次音频(频率可以控制)
# 播放音频!
# 生成随机数
i = random.randint(0, len(soundList) - 1)
# 此处的播放音频, 消耗时间比较多, 可能会引起输入的卡顿(不流畅)
# 可以创建一个线程, 在线程里播放音频!!
# playsound(soundList[i])
t = Thread(target=playsound, args=(soundList[i], )) # 给线程安排任务
t.start() # 启动线程
# 创建一个监听器对象,监听键盘的行为
listener = keyboard.Listener(on_release=onRelease)
print(type(listener))
# 启动监听器,但它运行在后台线程中。
listener.start() # 代码不会卡在这里,会立即继续往下执行
# 让主线程等待监听器线程结束。
listener.join() # 如果没有这行,主线程执行完所有代码后会直接退出,导致后台的监听器也被迫关闭。

此时就非常流畅了。




3. 【综合案例】学生管理系统
3.1 需求说明
实现一个 命令行版本 的学生管理系统。
【功能】
- 新增学生
- 显示学生
- 查找学生
- 删除学生
- 存档到文件
3.2 创建入口函数
- 使用一个全局列表 students 表示所有学生信息。
- 使用 menu 函数和用户交互. 这是一个自定义函数。
- 使用 insert , show , find , delete 这几个自定义函数完成增删查操作。
- 使用 sys.exit 实现程序退出。
# 实现一个命令行版本的学生管理系统
# 使用列表存储所有的学生
students = []
def main():
"""
程序的入口函数
"""
print('+--------------------------+')
print('| 欢迎来带学生管理系统! |')
print('+--------------------------+')
while True:
# 通过 menu 函数来打印出菜单项
choice = menu()
if choice == 0:
print('goodbye!')
sys.exit() # 退出程序
if choice == 1:
insert() # 新增学生
elif choice == 2:
show() # 显示所有学生
elif choice == 3:
find() # 查找学生
elif choice == 4:
delete() # 删除学生
else:
print('您的输入有误! 请重新输入!')
# 需要进入下次循环, 让用户重新输入
# continue
# 但是由于下面没有代码了,不使用continue,也会直接进入下一次循环
main()
3.3 实现菜单函数
def menu():
"""
显示程序菜单
"""
print(" 1. 新增学生信息")
print(" 2. 显示所有同学信息")
print(" 3. 根据名字查找学生信息")
print(" 4. 删除学生信息")
print(" 0. 退出程序")
choice = input(" 请输入您的选择: ")
return int(choice) # 返回整数,则main函数内的条件判断用整数
#return choice # 返回字符串,则main函数内的条件判断用字符串

3.4 实现增删查操作
(1)新增学生
def insert():
print("[新增学生] 开始!")
# 信息录入
studentId = input("请输入学生的学号: ")
name = input("请输入学生的姓名: ")
gender = input("请输入学生的性别: ")
if gender not in ('男', '女'):
print("性别不符合要求! 新增学生失败!")
return
className = input("请输入学生的班级: ")
# 使用一个字典存储学生信息
student = {
'studentId': studentId,
'name': name,
'gender': gender,
'className': className
}
# 把字典添加到学生列表中
global students
students.append(student)
print("[新增学生] 完毕!")
(2)显示学生
def show():
"""
遍历全局变量的这个列表, 把每个学生的信息给打印出来.
"""
print("[显示学生] 开始!")
# 遍历列表,格式化打印学生信息
for s in students:
print(f"[{s['studentId']}]\t{s['name']}\t{s['gender']}\t{s['className']}")
# 外双内单
print(f"[显示学生] 完毕! 共显示了 {len(students)} 条记录!")

重启程序,刚刚录入的数据就没有了。

(3)查找学生
def find():
"""
根据学生姓名, 来进行查找
"""
print("[查找学生] 开始!")
name = input("请输入要查找的同学姓名: ")
count = 0
for s in students: # 读取全局变量,不需要global声明,修改才需要
if name == s['name']:
print(f"[{s['studentId']}]\t{s['name']}\t{s['gender']}\t{s['className']}")
count += 1
print(f"[查找学生] 完毕! 共查找到 {count} 条记录!")




(4)删除学生
def delete():
print("[删除学生] 开始!")
studentId = input("请输入要删除的同学学号: ")
# 看看这个学号对应的同学是哪个字典, 然后把这个字典从列表中删掉就好了!
count = 0
for s in students:
if studentId == s['studentId']:
print(f"删除 {s['name']} 同学的信息!") # 外双内单
students.remove(s)
count += 1
print(f"[删除学生] 完毕! 共删除 {count} 条记录!")
每个学生都是一个字典,包含4个键值对。
pop按照下标删,remove按照元素删。

完成了增删查的功能,但是数据保存在变量中,变量保存在内存中,而内存容易丢失数据。
为了让数据能够持久化保存,需要把数据保存到文件中。
3.5 加入存档读档

(1)约定存档格式
约定存档文件放到 d:/…/record.txt 文件中。
并且以行文本的方式来保存学生信息。格式如下:
学号\t名字\t性别\t班级
学号\t名字\t性别\t班级学号\t名字\t性别\t班级
每个同学占一行。
每个同学的信息之间使用 \t 制表符进行分隔。
(2)实现存档函数
def save():
"""
存档函数
"""
# 此处的路径不是以 d: 开头的 "绝对路径" , 而是相对路径.
# 此时这个写法的含义就是让 record.txt 和 当前的 code66.py 在同一个目录里.
with open('record.txt', 'w', encoding='utf8') as f: # 上下文管理器with
for s in students:
f.write(f"{s['studentId']}\t{s['name']}\t{s['gender']}\t{s['className']}\n")
print(f"[存档成功] 共存储了 {len(students)} 条记录!")
在 insert 和 delete 末尾,调用 save 函数进行存档。
# 执行存档
save()

存档操作按写方式打开文件,文件不存在会创建文件,所以不需要先创建文件。


工作区如果看不到record.txt,可以刷新一下:

(3)实现读档函数
def load():
"""
读档函数
"""
# 如果存档文件不存在, 则直接跳过读档流程.
# 为了避免读方式打开文件的时候, 文件不存在引起异常.
if not os.path.exists('d:/record.txt'):
return
# 读档的时候要保证先把旧的数据给清理干净!
# 先清空全局变量里的数据
global students
students = []
# 读的方式打开文件
with open('d:/record.txt', 'r') as f:
for line in f:
# 针对这一行数据, 按照 \t 进行切分操作
# 切分之前, 要去除末尾的换行.
line = line.strip()
tokens = line.split('\t') # 切分结果放到一个列表中
if len(tokens) < 4: # 切出来不是4段
print(f"文件格式有误! line={line}")
continue
student = {
'studentId': tokens[0],
'name': tokens[1],
'gender': tokens[2],
'className': tokens[3]
}
students.append(student)
print(f"[读档成功] 共读取了 {len(students)} 条记录!")

读档函数在 main 函数开头的地方,调用 load 加载存档。
load()


3.6 打包成 exe 程序发布
当前虽然已经实现了一个管理系统,但是 .py 的文件只能在安装了 Python 环境的机器上运行。
为了能够更好的部署到其他主机上,可以借助 pyinstaller 来把 Python 程序打包成 exe 程序。
(1)安装 pyinstaller
pip install pyinstaller

(2)打包程序
- -F 表示打包成单个 exe(不带动态库)
pyinstaller -F 学生管理系统.py
【注意】如果提示找不到 pyinstaller 命令,则需要重启一下 PyCharm。
稍等片刻,很快打包完成。



此时就可以把这个程序拷贝给其他机器使用了。无需 Python 环境即可运行。



4. 后续扩展
4.1 python cookbook
python 经典进阶书籍,针对各种典型场景提供了一些解决方案。

4.2 awesome-python
整理了 Python 的一些非常实用的程序库。
https://gitee.com/awesome-lib/awesome-python
4.3 500 Lines or Less
使用简短的 Python 代码来实现一些有意思的程序。
https://github.com/aosabook/500lines
更多推荐


所有评论(0)