date
icon
password
博客链接
Pin
Pin
Hide-in-Web
Hide-in-Web
网址
type
slug
tags
category
bottom
bottom
Hide-in-Config
Hide-in-Config
comment
status
summary
如果本篇笔记对你有用,能否『请我吃根棒棒糖🍭 』🥺…
课程链接:【黑马程序员】 python 入门课程
『前言』
本人在学习黑马程序员的 python 前,曾学习过 C++(两年前),同时也在 小甲鱼 的课程中学过一次 python,因此个人认为较为简单的内容可能一两句话就带过了,请大家见谅。
虽然如此,但还是没能真正掌握这门语言,甚至还不能算是入门。小甲鱼的课程较为幽默,但是知识的讲解和传授说实话并不如黑马程序员,知识点学完后过了几个月又忘记了,而黑马程序员对 python 知识点的讲解总是由浅入深,同时又有案例辅助练习,效果甚佳。正在学习黑马程序员 python 课程的大家一定要坚定自己的选择,努力学完课程;如果是学习小甲鱼课程的同学,也许他的方法更适合你们,只是不适合我罢了。
目前笔记已完结,本门课程的名称为 “8 天 python 从入门到精通” 其实是针对具有一定编程基础的人而言的(之前学习过 python 或学习过其他编程语言),对于没有基础的小伙伴可能要花费相对长的时间,不过没关系,只要每天坚持看,案例认真做,python 都能够掌握。
P.S. 建议大家使用电脑来查看我的笔记,会有最好的显示效果。
本份笔记适合用于复习和检索 python 相关的知识点,【Ctrl+F】 唤起搜索框,输入关键字即可跳转到对应的知识点(关键字名称以黑马程序员课程中使用的知识点名称为准)。
【Python 入门前】黑马程序员课程笔记
准备工作:
课程中使用的截图软件
课程中使用的是
snipaste
截图软件,在 windows 自带的 microsoft store 中就可以下载。不过我推荐大家另外一个截图软件 ——
pixpin
,不仅拥有 snipaste 的全部功能,还能够实现截图文字识别,箭头转弯等等实用的功能,个人认为是目前市面上最好用的一款截图软件了。pycharm 专业版的官方免费获取途径
pycharm 的专业版其实是可以免费获取的,而且是官方途径获取。
pycharm 官方允许学生免费使用专业版,但前提是我们要在学校中注册自己的学校邮箱,一般是以
edu.cn
结尾的,学校邮箱具体要怎么注册要根据你们学校的说明来,或者可以试一试网络搜索 你的学校名+邮箱注册
。注册一个学校邮箱在很多地方都会有好处。注册了学校邮箱后,可以上网搜搜“如何使用学校邮箱免费使用 pycharm” ,这个有很多教程,在这里我只是给大家提供一个方向。
python 的安装建议
如果未来是想要入门机器学习、深度学习领域的友友,建议使用 anaconda 安装和配置 python,anaconda 中可以搭建多种版本的 python 环境,并且相互之间不会发生冲突,可以随时切换,灵活性很高。而如果不使用 anaconda,电脑中一次只能存在一个版本的 python,如果需要安装其他版本的 python,需要卸载原来的版本避免环境冲突。
anaconda 安装完成后,按下
Win+R
输入 cmd 打开命令行窗口,在命令行中输入 conda create -n [环境名称] python=3.x
创建环境,其中 [环境名称]
可以自定义,3.x
表示你要创建的 python 版本。比如,你可以运行我这条指令创建 python,conda create -n py_learn python=3.9
。创建好环境以后,通过
conda activate py_learn
可以打开名为 py_learn
的环境,之后想要进行什么操作都可以通过 conda [指令]
的方式进行。anaconda 常用指令
conda create -n myenv python=3.8
指令可以快速创建一个名为 myenv
的虚拟环境,python 版本为 3.8.conda activate myenv
用于进入这个虚拟环境;conda deactivate
用于离开虚拟环境;conda env remove -n myenv
用于移除这个虚拟环境。conda env list
用于查看所有的虚拟环境。关于我网站中的代码显示
有时候网页没有渲染好,代码界面可能显示的是这样的:

正常来说代码块的显示是非常美观的,类似这样的:

官方课程资源
资料中包含课件、代码和文档。
最后一个链接 『黑马 python 课程总资料下载(1.2 G左右)』是前面 16 个章节的总资料。
基本概念介绍
📝 Class Notes
字面量
代码中,被写在代码中的固定的值成为是字面量。
如整数、浮点数(即小数)、字符串(凡是引号包裹的内容都是字符串)等。
13、14.12、“你好,世界” 这些都属于是字面量。
数据类型

标识符
标识符指的是变量名、类名和方法名(函数名)、模块名。
变量的命名规范:
- 见名知意;
- 擅用下划线(变量名为多个单词的组合时);
- 变量名中的英文字母应全部小写。
标识符的命名规则如下:

关键字:

标识符的几个特点:
- 大小写敏感;
- 不能够使用关键字(关键字也是大小写敏感的);
- 只能够使用字母数字下划线。
运算符
算术运算

注意,
python
中的除法和 C++
中的略有不同,python
中的除法(/
)返回结果为浮点数,而 C++
中为整数。比较运算
布尔类型的数据可以通过比较运算得到,比较运算共有 6 种,如下表所示:

在 python 中,比较运算可以是如下形式:
上述的形式与 C++ 中的差异比较大,在 C++ 中,如果要进行这样的判断,需要这样写
if (a >= 2 && a <= 5)
。【补充】逻辑运算
与 C++ 不同,python 中没有&&
和||
这样的运算符表示 与 和 或,同时,C++ 中的&&
和||
运算符返回的结果似乎只能是True
和False
。
逻辑运算主要有三个:and
or
和not
and
和 or
返回的是实际操作数的值,而非强制转换为布尔值。not
始终返回布尔值 True
或 False
。and
运算符- 所有条件为真时返回最后一个操作数的值;
- 如果第一个或第二个条件为假,直接返回第一个或第二个操作数,不再判断后面的值,其他以此类推。
规则:
示例代码:
or
运算符- 只要任一条件为真时返回
True
,否则返回False
。 - 如果第一个条件为真,直接返回第一个操作数,不再计算第二个。
规则:
示例代码:
3.
not
运算符规则:
对布尔结果取反,将
True
转为 False
,False
转为 True
。示例代码:
python 中 and or 和 not 的优先级:
not
> and
> or
。不过建议在多个逻辑运算符共同使用时还是使用小括号标明优先级,小括号中的内容拥有高优先级,嵌套在越里面优先级越高。
如
a = 5 and ("python" or (("" or "True") and (13 or False)))
,返回的结果 a 的值为 "python"
。【补充】按位运算符
按位运算需要有一些数电基础,不过后面的课程中也不会用到,仅作补充。
&
和 |
在 Python 中是按位运算符,仅用于整数的二进制位操作。转义字符
在 Python 中,转义字符以反斜杠
\
开头,用于表示特殊符号或不可直接输入的字符(如换行、引号等)。以下是常见转义字符及输出转义字符的方法:常见转义字符
转义字符 | 作用 | 示例 | 输出结果 |
\n | 换行 | print("A\nB") | A (换行)B |
\t | 制表符(Tab 缩进) | print("A\tB") | A B |
\\ | 表示反斜杠本身 | print("C:\\路径") | C:\路径 |
\' | 单引号(避免字符串结束) | print('It\'s OK') | It's OK |
\" | 双引号(避免字符串结束) | print("He said \"Hi\"") | He said "Hi" |
\r | 回车(光标回到行首) | print("Hello\rW") | Wello |
\b | 退格(删除前一个字符) | print("A\bB") | B |
输出转义字符的方法
如果想直接输出转义字符本身(如打印
\n
而不是换行),有以下方法:- 使用原始字符串(Raw String)
在字符串前加
r
或 R
,转义字符会失效:- 双反斜杠转义
用
\\
表示一个反斜杠:- 使用
repr()
函数
返回字符串的“官方表示形式”(包含转义字符):
- 特殊场景:打印反斜杠本身
直接输入两个反斜杠或使用原始字符串:
应用场景
- 文件路径:用原始字符串避免转义问题:
- 正则表达式:避免转义冲突:
- 多行字符串:结合三引号使用:
【补充】绝对路径和相对路径
路径(Path) 是文件的“地址”,告诉计算机去哪里找到文件或文件夹。
路径有两种表示方法:绝对路径和相对路径。
绝对路径指的是以根目录(带盘符)开始的路径,使用
盘符:/
(盘符如 D:/
或 C:/
或F:/
等)或 盘符:\\
(这里之所以是两个正斜杠是因为在 python 中 \
是转义字符)表示。如 D:/test
就表示根目录下的 test
文件夹(D:\\test
也可以表示根目录下的 test
文件夹)。相对路径指的是文件或文件夹相对于当前代码文件存放的文件夹的路径,比如代码文件存放在
D:/test
文件夹下,那么在这个文件夹中的 hello.txt
文件就可以表示为 hello.txt
,这个路径下的文件夹 folder
中的文件 hello2.txt
就可以表示为 D:/folder/hello2.txt
。另外还需要知道两个符号
./
和 ../
。./
表示的是当前文件夹,D:/test
文件夹下的 hello.txt
文件就是 ./hello.txt
,这个路径也是相对路径,通常用于要在当前文件夹下创建新文件时使用。../
表示的是当前文件夹的上一级文件夹,比如 D:/test
文件夹的上一级文件夹就是盘符 D:/
,因此假如我们使用路径 ../test2
就表示 D:/test2
的意思。在 python 中,路径需要放在引号中作为字符串,因此,除了
盘符:/
和 盘符:\\
两种方式表示路径外,也可以使用 r"盘符:/..."
来表示路径,前缀 r 表示忽略引号中所有的转义字符。比如 r"D:\test\hello.txt"
。基础语法
📝 Class Notes
Python 中规范是软要求,语法是硬要求。代码是否符合规范能够反映出程序员编程水平的高低,有些代码即便能够运行,但不符合规范也会让人觉得很 low。
Python 中的快捷键
常识性快捷键
看到弹幕中很多小伙伴似乎刚接触电脑,对于一些常识性的快捷键还不清楚,因此在这里进行补充说明。
Ctrl+C
:复制鼠标选中的内容;Ctrl+V
:粘贴刚刚复制的内容;Ctrl+Z
:撤回上一步(连续按 Ctrl+Z
能够多次撤回);Ctrl+Y
:取消撤回(连续按 Ctlr+Y
能够多次取消撤回);Ctrl+S
:保存;Ctrl+F
:搜索当前活动区域;Tab
:制表符,能够产生一个缩进(一般在键盘的字符 “Q” 左侧)。Tab
:快速缩进;Ctrl+R
:搜索并替换;Ctrl+/
:将鼠标选中部分注释;Ctrl+[
:将当前光标移动到代码块开头;Ctrl+]
:将当前光标移动到代码块尾部;Ctrl+Shift+[
:将当前光标位置直至代码块开头位置的字符全部选中;Ctrl+Shift+]
:将当前光标位置直至代码块尾部位置的字符全部选中;Shift+Alt+鼠标左键
:以矩形区域(多光标)方式框选内容;【或者也可以双击
Ctrl
键,使用上下箭头的方式制造多光标,配合 Shift
进行矩形区域框选】Ctrl+Shift+Alt+鼠标左键
:可以在不连续区域用鼠标多次框选多个文本;Ctrl+P
:显示当前调用方法的所有参数;Alt+Enter
:选择并导入当前语句未定义的包或模块;(如 输入 time.sleep(5)
时,如果没有导入 time
模块,按下 Alt+Enter
就可以选择并加入 import time
语句)Python 常识
python 中的”标点符号”
python 中的所有“标点符号”都需要在英文输入法下进行输入,否则会报错,最常出错的就是冒号、括号和引号了。
⭐ 缩进
在【编程规范】中,缩进是四个空格。
缩进表示的就是一条语句前面的间距,缩进的大小表示空格的个数。
在 python 中,缩进是非常被看重的,缩进表示的是层级,缩进既可以是 1 个空格,也可以是 4 个空格(默认使用 4 个空格),一旦确定了缩进中空格的个数,同一层级中就要保持一致,否则会报错。
注释
单行注释:通过 “
#
” 产生注释,在【编程规范】中,“#” 和注释内容间需要加上一个空格,注释的符号“#” 前面若有代码,需要与代码有两个空格的距离。多行注释: 以三个双引号(“”“)开头及三个双引号(”“”)结尾。
如:
人机交互
打印输出
print
语句默认是以换行符('\n'
)结尾的,也就是在 print 语句执行完后,下一条语句的打印输出结果是在下一行出现,如果我们并不希望 print
后输出立即换行,让 print
语句以空字符结尾需要使用下面的方式。
如果想要通过一个
print
语句输出换行或是其他效果,可以通过转义字符实现。接收输入
input(交互内容)
语句可以实现外部数据的接收。
需要注意的是,外部接收到的数据(包括数字) python 都会默认将其转换成字符串类型。
数据类型
查看数据类型的方法 【type()
】
type()
语句可以查看变量的类型,使用方法是在括号中放入需要查看类型的变量。如:
要记住,变量本身没有类型,但变量存储的数据是有类型的。例如上述代码中的
name
是变量,没有类型,而 name
中存储了值 '世界'
后,它就可以称为是字符串变量了;如果存储了整数,就称为是整型变量。通常类似于字符串变量这样的说法指的都是变量存储的数据类型是字符串。
数字类型
数字类型主要有整数、浮点数(理解为小数)、复数(以 j 结尾,如
3+4j
)、布尔数(True
、False
)这四类。整数和浮点数:
整数与浮点数在定义的时候,区别在于整数不会加上小数点
.
【小数点 .
在中文输入法下为句号 。
,但在英文输入法下为一个点 .
,即英文的句号】,而浮点数无论小数点后面是否有零,都必须加上小数点。布尔数:
布尔数的特殊之处在于它只有两个值 ——
True
和 False
。用于逻辑判断,比较运算的返回结果都是布尔数,而逻辑运算的返回结果部分是布尔数(根据具体情况,只出现数字的逻辑运算返回结果都能够转换为布尔数)。
布尔数主要用于后面学习的循环和条件判断语句中,到后面会有更深的理解。
数据类型的转换【str()
int()
float()
bool()
】
str()
会将括号中的内容转换为字符串;
int()
会将括号中的 整数、布尔数及数字字符串 转换为整数,无法转换科学计数法表示的数据(如int(3.11E+5)
会报错);
float()
会将括号中的 整数、布尔数及数字字符串 转换为 浮点数,可以将科学计数法的表示的数字转换为数字,如float(3.11E+5) → 311000.0
。
bool()
会将括号中的 整数、布尔数及数字字符串 转换为布尔值True
和False
,所有的 非零数字(整数和浮点数)都会转换为True
,零会转换为False
。
数字字符串是我自己起的名,不知道专不专业,表示的是字符串两个引号中只有数字的字符串,如
"33.0"
、"2"
。字符串类型(字符串拼接,数字精度控制)
字符串定义
既可以使用单引号和双引号定义,也可以使用三引号来定义。如果三引号前面有变量进行赋值,则三引号用于定义字符串,如果前面没有变量进行赋值,则三引号用于多行注释。三引号定义字符串的好处在于可以换行。
字符串拼接(加号拼接) → 性能一般,可读性一般
加号拼接仅用于字符串与字符串间,字符串变量与字符串变量间,字符串与字符串变量间。

字符串的拼接可以使用加号来完成,字符串既可以直接拼接,也可以存放在变量中进行拼接。
但是要记住的是,数字、浮点数等数据存放在变量中是无法通过加号与字符串进行拼接的,但可以将它们转换为字符串类型后用加号拼接。
【字符串拼接】字符串格式化 - 占位符(%d
%s
%f
) → 性能一般,可读性一般
字符串占位符拼接是将变量转换为字符串变量后再进行拼接。
占位符的使用方法
常用的三种占位格式符号:

具体介绍:

使用三种不同占位符的代码示例:

数字精度的控制

占位符搭配表达式的应用
通过表达式进行格式化操作。
表达式指的是一条具有明确执行结果的代码语句。如 1+1,5 * 2 这种算式就属于是表达式。
【字符串拼接】字符串格式化 - f"内容{变量}"
→ 性能高,可读性高
【补充】join
方法拼接字符串 → 性能高,可读性高
join
方法是 python
的内置方法,用于字符串,它的语法如下:代码示例:
- 拼接字符串列表
- 拼接字符串元组
- 拼接字符串集合
- 拼接单个字符
【补充】字符串重复
假如现在我有这么一个需求:
给定一个字符串str
和一个非负的int
数n
,返回一个新的字符串,其值为原字符串重复n
次。
我该如何操作呢?如果运用之前学习的字符串方法,要实现并不容易,这里就需要用到字符串重复的语法。
字符串重复的语法很简单,将字符串乘上需要重复的次数即可,如下方的代码所示:
布尔类型
布尔类型的数据只有两种结果,一种是
True
,另一种是 False
。True 表示的是真(使用 1 来存储),False 表示的是假(使用的是 0 来存储)。因此它们也可以算是数字类型。布尔类型的数据可以通过比较运算得到,比较运算共有 6 种,满足比较的条件为 True ,不满足则为 False。值得注意的是,
==
运算符可以进行字符串的比较,如果两个字符串是一样的,则返回 True
,否则返回的是 False
。None 类型

整数类型表示的一个小妙招:当我们需要赋值的数字位数很多时(如 5000000),阅读代码时不一定一下子能够看出来数字是多大,因此在赋值的时候可以灵活使用下划线进行数字的分隔(建议每三个数字使用一个下划线),比如前面的数字可以写为 5_000_000,这样我们就能一眼看出数字的大小了。
语句类型
不同的语句之间都是可以相互嵌套的,一定一定要记得缩进。
条件判断语句
单条件判断语句

使用判断语句的时候有两个注意点:
- 要记得判断条件后面的冒号;
- 要记得加入缩进(缩进表示层级,默认是四个空格缩进为一个层级)。
单条件判断语句(包含条件不满足时需要执行的代码)
if…else…
语句:多条件判断语句
if…elif…else…
语句:
代码示例如下:

在上述代码中,可以将
input
语句直接写入判断条件中,节省代码量【不过弹幕中建议不要这么做,特别是在工作中,可读性差】。嵌套的条件判断语句
嵌套指的是在 if 语句的代码块中再写一个或多个 if 判断语句。
对于嵌套的条件判断语句,更要注意缩进的应用了,同一层级的 if else 语句使用的缩进大小相同。
代码示例:

循环语句
while 循环
随机数生成语句(random)
语句介绍及简单案例

代码示例 1 :
代码示例 2 :
嵌套(while)循环
嵌套的 while 循环和嵌套的条件循环是一样的,都是通过缩进来控制层次关系的。


九九乘法表代码示例:
for 循环
for 循环与 while 循环的区别是前者是 遍历循环(理解为“挨个循环”),后者是条件循环。
for 循环的介绍及简单案例
for 循环是将序列类型(如字符串)中的数据一个个取出来进行操作的,因此 for 循环理论上无法构建无限循环,因为数据集一般是有限的。

代码案例:

案例求解:
快速生成数字序列(range)
大多数是用于 for 循环语句中。

简单案例:

案例求解:
for 循环中的临时变量

四行九九乘法表
弹幕中说通过 for 循环搭配 range 仅用四行语句即可生成九九乘法表,我试了一下,真的可以。
range 和 random 语句
这两个语句容易弄混,总结了一下,主要有以下不同:
- 前者是用于生成序列的,后者是用于生成随机数的。
- 前者括号中可以只有一个参数,也可以有两个、三个参数,如 range(10) 表示生成 0-9 的序列,range(1, 10) 表示生成 1-9 的序列,range(1,10,2) 表示生成步进为 2 的序列 [1,3,5,7,9];而后者只能使用两个参数。
- 前者使用两个参数时,如 range(a, b) 范围为 a 到 b-1;而后者使用两个参数时,如 random.randint(a, b) 范围为 a 到 b。
循环语句中断
#continue 语句 和 break 语句。

综合案例(发工资)

老师的代码:

函数
函数的作用就是降低代码的冗余,让代码简洁、优雅,提高可读性强。
函数的介绍
python 中的函数与数学中的函数有异曲同工之妙,数学中的函数(如 y = 2x)是传入一个参数 x 的值就能够得到 y 的值,而 python 中的函数是传入若干个参数(参数也可以省略)后执行相应的代码块。

【函数的定义及调用】最基础的函数定义
函数的介绍可以由简入深,先从最简单的函数结构说起,慢慢接触复杂的函数体。
函数定义就如同数学中写出了数学公式(如 y = 2x),要得到 y 的内容需要我们去主动调用这个函数,因此,在函数定义后需要调用才能够执行相应的代码块。
【函数的定义及调用】稍复杂的函数定义
加入函数的传入参数,函数的传入参数可以理解为数学函数中传入参数 x。函数的传入参数可以有若干个,可以理解为数学函数中的多元函数,如函数有两个参数时类比为数学函数中的(z = x^2 + y^2)。
形式参数和实际参数:
形式参数类似于数学函数(如 y = 2x)中的 x,实际参数类似于 x 的实际值,如 x = 2 时实际参数为 2。

【函数的定义及调用】完整版的函数定义
完整版函数定义包括传参和返回值。

在返回值的过程中,如果有参数接收(如上图
r = add(1, 2)
中的 r
),那么相当于 r = result
;如果没有参数接收(即只有 add(1, 2)
),也不会报错。
上图中的最后一句话比较重要,函数中代码块执行到
return
后就结束了,类似于循环语句中的 break
语句。其实 python 定义的任何函数都是有返回值的,如果我们不写 return 语句,实际上返回的就是 None ,这是 python 默认帮我们加上的。
如何验证 python 中默认返回的是 None?

None 类型的应用场景:

函数说明文档
函数说明文档可以理解为是函数里面的注释,用于说明整个函数是做什么用的,帮助别人在不阅读函数具体内容的情况下也能快速理解并使用函数。

函数的嵌套调用
函数的嵌套和前面的条件判断语句的嵌套以及循环语句的嵌套是一样的,就是在函数定义的代码块中的调用另一个函数。

代码示例:

变量定义域【局部变量,全局变量,global 关键字】
变量作用域指的是变量的作用范围(变量在哪里可用,在哪里不可用)。
局部变量:作用范围在函数内部,在函数外部无法使用。

全局变量:在函数内部和外部都可以使用。

如何将函数内部的变量声明为全局变量?
使用 global 关键字。

综合案例

⭐【辨析】函数与方法
函数与方法本质上是一样的,只是二者出现的位置不同。
方法是定义在 类 中的函数,至于什么是类,在后面的课程中会介绍,这里可以把类简单理解为一个“贴有标签的篮子”,“篮子”中装有许多预先定义好的函数和变量。当我们根据标签把篮子取出时,篮子中所有的函数和变量都可以供你使用。将类中的函数称为方法主要是为了 区别 类以外定义的函数。
【数据容器学完后】函数进阶
函数的多返回值
多 return 的情况:
对于有多个 return 语句的情况,我们需要谨记一点:只要执行了其中一个 return 语句就会退出函数,不再执行其他代码。
return 多个值的情况:

图中函数多返回值返回给
x
和 y
变量利用到了之前提到过的元组解包。【忘记了 Ctrl+F 搜索 “元组解包”】函数的多种传参方式
主要有 4 种 —— 位置参数、关键字参数、缺省参数、不定长参数。
- 位置参数:

- 关键字参数:

在调用函数的实际参数时,如果位置参数和关键字参数混用,那么位置参数必须在关键字参数的前面。
- 缺省参数:

这里再强调一下,在函数的形式参数中,所有的位置参数(没有 ”=”)必须出现在默认参数(有 “=”)之前,也就是有 ”=” 默认赋值的参数必须放在最后。
如果有多个 “=” 赋值的参数,那么它们都要放在没有 “=” 赋值参数的后面,这些 “=” 赋值的参数顺序无所谓。
- 不定长参数:
- 位置传递:
- 关键字传递:
不定长参数:不定长参数也叫可变参数。用于不确定调用的时候会传递多少个参数(不传参也可以)的场景)。
作用:当调用函数不确定参数个数时, 可以使用不定长参数。
不定长的参数主要分为两类:① 位置传递(
*args
);② 关键字传递(**kwargs
)。传入的多个参数合并为 元组。

args 的含义为 arguments,意思是参数,在规范下,不定长参数的位置传递均使用
*args
表示。传入的多个参数合并为字典。

kwargs 含义为 keyword arguments,应该是键值对的意思,在规范下,不定长参数的关键字传递均使用
**kwargs
表示。函数本身作为参数传递
将函数本身作为参数传递一开始容易让很多人措手不及,主要面临两个问题:
- 将函数作为参数传递和将数据作为参数传递有什么区别?
- 将函数作为参数传递与函数的嵌套有什么区别?
上述两个问题在介绍完本部分内容后会解释,首先来看代码:
问题 2 的解决:将“单纯的函数嵌套”与上面“将函数作为参数传递”进行对比,很容易发现,前者只能执行代码块中的加法运算,如果要实现减法运算,就还需要另外定义一个函数;而后者根据传入函数的逻辑不同,仅通过一个函数就能够实现加法、减法等多种运算,是不是更加灵活呢?
问题 1 的解决:将函数作为参数传递是一种计算逻辑的传递。
怎么理解“逻辑”这个概念呢?
比如加法逻辑,指的就是执行 a + b 这个操作;减法逻辑,指的就是执行 a - b 这个操作;你也可以定义新的计算逻辑,比如 混合运算 x+y*z 。
当然,函数的逻辑不仅仅是执行数学运算的,也可以是其他我们过去学习的循环、条件等等代码块的集合。
而数据的传递仅仅是字面量的传递,不包含代码逻辑。
lambda 匿名函数
lambda 匿名函数,顾名思义,就是没有函数名的函数,由于没有函数名,所以在后面的代码中无法通过函数名显式调用匿名函数,因此匿名函是临时函数,只能在定义的位置临时调用一次。
lambda 与 def 一样,都是定义函数的关键字。

在 lambda 函数的函数体如果是一个数学表达式(包括算术运算和比较运算),那么会作为计算结果会作为返回值返回。
lambda 函数在现实应用中是非常广泛的。比如在后面的 PySpark 库中就经常使用。
数据容器
学习数据容器,就是为了批量存储和批量处理数据,使用数据容器可以让代码看上去更简洁、美观、优雅。
数据容器的介绍

注意上图的叙述,数据容器共有 5 类——列表、元组、字符串、集合、字典。
列表([ ]
list()
)
列表定义
使用中括号 “[ ]” 来定义列表


嵌套列表
经过前面很多次嵌套的介绍,想必大家对嵌套也有了一定的理解,与前面条件判断中的嵌套、循环嵌套、函数嵌套一样,列表的嵌套就是将列表中的某个元素使用列表来表示。

列表的嵌套可以用来实现二维数组,这也是为什么 python 中没有数组类型的原因。
如果我们想要取出嵌套列表中的某个值,可以通过两级索引的方式,比如,我想要取出 5:
my_list[1][1]
。列表索引
列表索引有两种方式,一种是顺序索引(从左向右,从 0 开始),一种是倒叙索引(从右向左,从 -1 开始)。

在进行下标索引的时候,要注意下标索引的取值范围,超出范围无法取出元素,并且会报错。
列表方法
当一个变量是 list 类型时,它就属于是 list 类,可以调用 list 类中的各种方法(各种函数)。
常用的一些方法如下:

这些方法不一定全要硬背下来,但是至少要留下个模糊的印象,知道有能够方便实现列表操作的方法在,这样,未来即便忘记了方法怎么使用,也能够通过自己的笔记或上网搜索得到答案。
.pop(下标)
方法在删除下标元素后会返回删除的元素值。.pop()
方法括号中如果没有使用下标,默认是删除最后一个元素,未来在使用 栈 解决问题时很有用。列表的特点

【自构案例】银行系统的完善
在弹幕中看到有人利用新学习的列表方法完善了之前的银行系统,增加了用户的注册、登录、修改密码、注销账户的功能,觉得挺有意思的,于是自己也尝试了一下。
列表的遍历
遍历指的是将容器内的元素依次取出,并处理。
可以通过前面学过的循环语句(while 和 for)来实现。
案例:

代码:
元组(( )
tuple()
)
列表与元组的差异在于,列表中的内容是可以修改和替换的,而元组中的数据一般是无法直接修改的。当我们需要在程序内封装数据,又不希望封装的数据被篡改,那么元组就非常合适了。
元组定义
元组使用英文小括号
()
来定义,列表使用 []
来定义。
需要特别注意一点,在定义元组时,若元组只有一个元素,需要在元组末尾加上英文逗号进行分隔。如
my_tuple = (2, )
元组也可以不加上小括号来定义,如
my_tuple = 1, 2, 3
就定义了一个元组 (1, 2, 3)
,若元组只有一个元素,同样需要在末尾加上一个英文逗号。但是不加括号定义元组的方式不太符合正常使用习惯,容易让人产生误解,最好不要使用。
元组嵌套
与列表类似,元组也可以通过类似的方式实现嵌套。
一般来说,元组中的数据是不可修改的,但在课堂中老师举了一个特殊的例子——元组与列表的嵌套,这种情况下,列表中的元素仍然可以修改。
元组方法
元组的操作方法与列表中的相似,基础的有三个 —— index(),count(),len(元组)。

上述三个方法在列表方法中也有,但它们不会修改元组中的内容。
元组的特点

元组的遍历
元组的遍历与列表的遍历一样,都是使用 while 和 for 循环。

元组案例
练习案例:

代码:
【补充】交换赋值(元组解包)
在 python 中,可以轻松的实现两个变量值的交换,如
a, b = b, a
的意思就是将 a 的值赋给 b,同时将 b 原来的值赋给 a。在其他语言中,如 C++ 中,要想实现交换赋值必须要引入第三个变量,具体的代码如下:
pyhton 实现交换赋值的原理是元组解包:
所谓的元组解包,就是把多个值从元组(或列表、字符串等)里一次性“拆开”,按顺序赋值给不同的变量。
当执行
a, b = b, a
时,Python 解释器会按以下步骤操作:- 先计算右侧表达式:
右侧的
b, a
会被隐式转换为一个元组 (b, a)
,并完整保留 a
和 b
的当前值。- 按顺序解包赋值给左侧:
左侧的
a, b
会依次接收元组中的值:字符串(""
、''
、 str()
)
字符串与元组类似,字符串中的内容是不支持修改的,在对字符串作出某种操作后,只能用一个新的字符串来接收。
字符串的新面孔
字符串也可以看作是数据容器,因为它是字符的容器,能够通过下标对字符进行索引。
字符串的索引值如下图所示:`

字符串方法

代码示例:
【补充】字符串的其他方法
- 字符串拼接
"xxx".join(列表)
:可以将列表中的每个元素都以xxx
为分隔符进行拼接。
但需要注意的是,列表中的每个元素都必须是字符串类型。
- 大小写转换
.upper()
:将字符串中的所有字符转换为大写。.lower()
:将字符串中的所有字符转换为小写。.capitalize()
:将字符串的第一个字符转换为大写,其余字符转换为小写。.title()
:将字符串中每个单词的首字母转换为大写。.swapcase()
:将字符串中的大写字母转换为小写,小写字母转换为大写。
- 查找与替换
.find(sub)
:返回子字符串sub
第一次出现的索引,如果未找到则返回-1
。.rfind(sub)
:返回子字符串sub
最后一次出现的索引,如果未找到则返回-1
。
find
方法与index
方法是类似的,但index
方法如果未找到子字符串会抛出ValueError
。
- 检查内容
.startswith(prefix)
:检查字符串是否以prefix
开头。.endswith(suffix)
:检查字符串是否以suffix
结尾。.isalpha()
:检查字符串是否只包含字母。.isdigit()
:检查字符串是否只包含数字。.isalnum()
:检查字符串是否只包含字母或数字。.isspace()
:检查字符串是否只包含空白字符(空格、制表符、换行符等)。
- 去除空白字符
.lstrip()
:去除字符串开头的空白字符。.rstrip()
:去除字符串结尾的空白字符。
与.strip()
方法类比,在strip
前面加上l
(即left
的缩写)表示只去除左侧的字符,加上r
(即right
的缩写)表示只去除右侧的字符。
- 其他常用方法
.center(width)
:将字符串居中,并用空格填充到指定宽度。.zfill(width)
:用0
填充字符串到指定宽度。
字符串遍历
与元组和列表一样,也是使用 for 和 while 循环
字符串的特点

⭐ 切片操作([ : : ]
)
什么是序列?
在前面的学习中,我们接触了很多次“序列”这个名词,比如使用 range() 语句生成序列。
序列的定义是:内容连续、有序,可使用下标索引的一类数据容器。
也就是说,列表、元组、字符串均可以视为是序列的一种。
对于一个序列而言,目前已知它有两个特点:
- 可以在 for 循环中进行遍历的操作;
- 可以进行切片操作。
切片操作
切片指的是从一个字符串中取出一个子字符串。
能够实行切片操作的关键在于数据容器能够使用下标索引容器中的元素。
语法: 序列[起始下标:结束下标:步长]

注意:此操作不会影响序列本身,而是会得到一个新的序列(列表、元组、字符串)。
切片操作演示:

上图中,颜色不同的那一条代码示例需要特别注意,这是将序列反转的最简单的方式。
案例
案例:

代码:
集合({ } set()
)
python 中的集合和数学中的集合是一样的,在数学中,我们知道集合的特点的是元素是唯一的,顺序是随机的,即无序性、互异性。
集合定义
基于集合的这个特点,python 中集合的使用场景就是要求容器中的数据都是唯一且无序的。
由于集合互异且无序的特性,集合是无法使用下标索引访问数据的,更无法使用切片操作。
⭐ 集合能够存放的元素
集合方法

集合的方法与列表的操作方法类似。与列表相比,由于集合有交集、并集的概念,因此多了交集、并集的相关方法;由于集合中的元素是无序的,因此没有类似于 append, insert, del, index 这样的方法;由于集合中每个元素都是唯一的,因此没有 count 方法。
综上,集合的方法其实是很好记忆的,大多数与列表重合,只需要多记三个集合的操作几个。
集合中还可以通过运算符来更方便的实现差集和并集,
-
表示做差集,|
表示做并集(注意不是 +
)。集合特点

集合的遍历
由于集合是无序的,因此集合无法通过 while 循环来遍历数据,只能使用 for 循环。
案例
案例:

代码:
字典({ : , : , …} dict()
)
python 中的字典类似于现实中的字典,现实中我们查询字典都是查到某个字看它的释义(不考虑多音字),只要字是确定的,那么释义就是确定的。
字典定义
python 中字典的每个元素称为是键值对,键值对间使用英文冒号分隔,对于唯一的键,它具有唯一的值。

这个和数学中映射的概念很像,对于一个函数而言,在输入确定的时候,输出是唯一且确定的,在不同的输入下,输出可以相同。键值对也是如此,在键确定的时候,值是确定的,不同键对应的值是可以相同的,只要键不重复即可。
字典索引
字典与集合一样,均无法通过下标进行索引,但可以通过“键”来索引元素。

字典嵌套
字典中,键除了字典类型以外可以为任意值,而值可以是包含字典在内的任意值,因此字典是支持嵌套的。

字典方法

注意表格中的第二条语句:
字典[Key] = Value
,这条语句既可以在字典中没有 key
的时候添加 value
,也可以在有 key
的情况下更新 Value
。在熟悉了列表、集合的方法后,字典的方法也不难记忆,很多还是和前面重复的。
字典的遍历
与集合类似,字典的遍历只能使用 for 循环。
字典特点

案例
案例:

代码:
【补充】定义空容器
定义空容器都可以使用各自的类型名来进行定义,也可以使用相应的括号(集合除外)来定义。
列表的类型名为:
list
;元组的类型名为:
tuple
;字符串的类型名为:
str
;集合的类型名为:
set
;字典的类型名为:
dict
;定义空集合必须使用
set()
方法,{}
表示定义空字典。五种数据类型的总结对比
- 5 类数据容器都支持 for 循环遍历;
- 列表、元组、字符串支持 while 循环,集合、字典不支持(无法下标索引)。

【补充】⭐ python 中的三种类型括号总结
经常在弹幕中看到有人弄不清楚三个括号的含义,在这里整理了一下。
括号类型 | 主要用途 | 示例 |
( ) | 元组、函数(方法)、表达式优先级 | (1, 2) 、func() 、(3+4)*2 |
[ ] | 列表、索引、列表推导式 | [1, 2] 、lst[0] 、[x for x in ...] |
{ } | 字典、集合、推导式 | {'a':1} 、{1,2} 、{k:v for ...} |
⭐ 数据容器的通用操作(.
sort
方法 和 sorted
函数)

【补充】⭐ .sort()
方法和 sorted
函数
主要区别:
sorted()
是内置函数,而sort()
是内置方法,内置函数和内置方法的调用方式不同;
内置函数:sorted(排序对象)
; 内置方法:排序对象.sort()
sorted()
不能修改原操作对象,而是会将排序后的列表返回给一个新的对象;sort()
直接对原操作对象进行修改。
基于第二条的特性,引入第三条区别。
- 由于
sort()
直接对原操作对象进行修改,因此sort()
只能对列表进行操作;而sorted()
是返回给一个新列表,因此对于五种容器类型都可以进行排序,即既可以对列表进行操作,也可以对元组、字典进行操作。
值得注意的一点是,sorted()
也可以对字符串和集合进行排序,它可以将字符串和集合中的每个字符提取出来进行排序,并返回给一个列表。
相同点:
都可以对列表进行排序,排序后的结果都是列表。
使用上述方法时的注意点:
max()
和min()
两种方法可以取出数据中的最大及最小字符,这里的最大及最小指的是 ASCⅡ 码的最大与最小(在后面会有更详细地介绍)。在计算机中,所有的字符都是通过 ASCⅡ 码来存储的;
- 在使用
list()
方法将字符串转换为列表时,会将字符串中的每个字符都转换为列表中的元素;
- 字典在使用
list()
、tuple()
、set()
操作转换时,键会保留,但值都会丢失;
- 对于
sorted()
方法而言,[reverse=True]
的中括号表明其是可选参数,可以不用赋值。使用sorted()
方法时,若不直接赋值可选参数reverse
,reverse
默认为True
,表示降序排列。在使用sorted()
方法时,若对象是字典,字典的值也会丢失。
ASCⅡ 表
在程序中,字符串所用的所有字符如:
- 大小写英文单词
- 数字
- 特殊符号(!、\、|、@、#、空格等)
都有其对应的ASCII码表值。常用字符对应的 ASCⅡ 码表值如下所示:

对于单个字符而言,通过 ASCⅡ 码即可判断大小;
对于多个字符而言,需要按位进行比较:

案例
案例:

代码:
类型注解
python 3.5 版本时新增的功能。
引入
当我们定义了一个列表(
list = [1, 2, 3]
)后,在使用这个列表时,python 能够判断出这个是一个列表变量,并提示我们可能会使用的方法:
而若 python 无法判断我们使用的变量的类型,那么它就不会给我们相应的代码提示:

同时,对于我们自定义的函数,如果
python
无法判断我们定义的函数的参数,在调用函数并使用快捷键 Ctrl+P
打开函数参数列表时,也不会有代码提示产生:
因此,为了能够在编写代码时方便 python 进行类型判断,也为了增加我们代码的可读性,我们需要对一些难以确定类型的变量进行类型注解。
类型注解主要分为两类 —— 变量的类型注解和函数的类型注解。
变量的类型注解
变量的类型注解有两种方式。
可供使用的类型名称(常用):int
→整数,float
→小数,str
→字符串,bool
→布尔值,list
→列表,tuple
→元组,set
→集合,dict
→字典。
- 第一种方式 ——
变量:类型
- 第二种方式——注释中进行注解
type:类型
函数的类型注解
函数也有两种方式进行注解。
第一种是直接在函数定义的形参列表中进行注解;第二种是在函数的注释中进行注解
【容器中】详细类型注解及混合参数类型注解
对于容器类型的数据,可以进行详细的类型注解,主要是说明容器内的元素类型。
混合参数下的详细注释:
混合参数的注释需要引入一个新的注释符号Union
,要使用Union
,必须先导入typing
模块。
在函数中也可以进行混合参数类型注解,与列表注解方法是一样的。
最后说明以下,类型注解不是必须的,即便没有类型注解,代码也能正常运行,只不过,为了提高代码的可读性,建议加上类型注解。
文件操作
在日常生活中,我们大多数人都使用过 word 写文档。文件操作的所有功能都和我们使用 word 的操作大同小异。
使用 word 的基本步骤都是先创建一个 word 文件,然后打开 word 文件,写字(该过程简称为“写”),写完后关闭文件。
如果别人发给我们一个 word 通知文件,那么我们就仅需要打开 word 文件,读取通知内容(该过程简称为“读”),读完后关闭文件。
无论如何,我们要操作一个文件,首先都需要打开文件,最后都需要关闭文件,具体是读还是写,还是读写同时进行需要我们根据自己的目的来选择。
文件编码
由于计算机只能够识别 0 和 1 两个二进制数,因此所有的数据都是通过二进制数来存储的,且计算机处理二进制数的效率高。

在之前我们提到过,ASCⅡ 编码是对数字、字母以及一些常用字符的编码,如字母 a 的 ASCⅡ 码就是 97,之所以要将字符通过 ASCⅡ 码来表示,也是因为计算机只能认识和处理二进制数,将 ASCⅡ 码转换为二进制数就能够方便存储字符了。
二进制编码的方式方式有很多种,目前世界上最常用的编码方式是 UTF-8 编码(涵盖了中文、英文、韩文、日文、表情等等多种字符),当我们决定使用某一种编码方式存储文件时,下一次打开此文件就要使用该编码方式,否则就会出现乱码。
文件类型
在文件读取时,我们需要先知道文件都有哪些类型:

文件操作相关方法

文件打开
打开文件(open()):


在打开文件时,需注意:
Windows的文件路径用 “
\
” ,但在python中 “\
” 是转义字符,所以需要用 “\\
” 转回去,或者使用 “/
” 字符。如 D 盘下的测试文件
test.txt
,其文件路径为 D:/test.txt
,也可以使用 D:\\test.txt
。mode 先介绍三种:

示例代码中的 f 我们可以理解为一个容器,类似于前面介绍的数据容器。对 f 进行操作就是对文件本身进行操作,f 可以使用一些 python 内置的“方法”进行操作。
文件关闭
文件关闭方法(close()):

文件读取方法
若要进行文件的读取,在打开文件的时候就要选择”读“的方式。

f.read()
方法读入的类型为 字符串,即将文件中的所有内容都放入一个字符串中进行储存;f.realines()
读入的值为 列表,文件中的每一行作为列表中的每个元素,列表中的每个元素都是字符串,以换行符 "\n"
结尾。文件在
open()
打开以后一定要记得关闭 close()
,否则文件会一直处于占用状态,在程序持续运行的情况下,文件无法被其他应用操作或修改,也无法删除。对于我们自己写的简单测试程序而言,我们当然可以通过停止程序运行来关闭文件,但对于无法随意停止的程序而言,这个问题就大了。如果怕自己会忘记写
close()
,那么 with open() as f
方法无疑是最好的选择。在打开一个文件且未关闭前,对文件的所有读取操作都是连续的,也就是说,如果打开文件后第一次读取内容为一行,文件并没有读取完,下一次读取会在上一次读完的地方接着读。
文件读取的案例
案例:

文件内容
itheima itcast python
itheima python itcast
beijing shanghai itheima
shenzhen guangzhou itheima
wuhan hangzhou itheima
zhengzhou bigdata itheima
代码:
文件写入
如果要写入文件,在打开文件时就要选择“写入”方式 “w“。 【open(”test.txt”, “w”)】

再提一下:在打开文件时,若文件已经存在,则原有内容被删除;若文件不存在,会创建新的文件。
文件写入方法(
.write()
):
在文件关闭的时候,会默认调用 flush 方法。也就是说,文件 close() 后,write 的内容会全部写入文件中。
⭐ .writelines()
方法及其与 write()
方法的区别
writelines()
方法:- 功能:将一个字符串列表(或其他可迭代对象)写入文件。
- 参数:接受一个可迭代对象(如列表、元组等),其中的元素必须是字符串。
- 返回值:无返回值(
None
)。
- 特点:
- 可以一次性写入多行内容。
- 不会自动在元素之间添加换行符,如果需要换行,必须在每个字符串中显式添加
\n
。如lines = ['Line 1\n', 'Line 2\n', 'Line 3\n']
。
write()
方法:- 功能:将单个字符串写入文件。
- 参数:接受一个字符串作为参数。
- 返回值:返回写入的字符数(整数)。
- 特点:
- 每次调用只能写入一个字符串。
- 不会自动添加换行符,如果需要换行,必须显式地在字符串中添加
\n
。
文件追加写入
与文件写入的所有方法一致,只不过在打开文件的时候选择 “
a
” 追加模式即可。如果想要输入文件的内容能够换行,可以使用转义字符 “
\n
” 。⭐ open
中的其他读写方式(mode)
"r"
只读方式、"w"
只写方式和"a"
追加方式是 python 中最常用的三种方式,除了这三种外,还有一些其他方式。
基本模式:
"x" | 仅创建文件。如果文件已存在,会抛出 FileExistsError ;如果文件不存在,会创建新文件。 |
组合模式:
"r+" | 读写模式。文件必须存在,可以从文件开头读取或写入内容。 |
"w+" | 读写模式。如果文件存在,内容会被清空;如果文件不存在,会创建新文件。 |
"a+" | 读写模式。如果文件存在,写入的内容会追加到文件末尾;如果文件不存在,会创建新文件。读取时从文件开头开始,写入时从文件末尾开始。 |
二进制读写方式:
在模式后加上
"b"
,表示以二进制方式操作文件(适用于非文本文件,如图片、视频等)。模式 | 描述 |
"rb" | 二进制只读模式。文件必须存在。 |
"wb" | 二进制只写模式。如果文件存在,内容会被清空;如果文件不存在,会创建新文件。 |
"ab" | 二进制追加模式。如果文件存在,写入的内容会追加到文件末尾;如果文件不存在,会创建新文件。 |
"xb" | 二进制独占创建模式。如果文件已存在,会抛出 FileExistsError ;如果文件不存在,会创建新文件。 |
"rb+" | 二进制读写模式。文件必须存在,可以从文件开头读取或写入内容。 |
"wb+" | 二进制读写模式。如果文件存在,内容会被清空;如果文件不存在,会创建新文件。 |
"ab+" | 二进制读写模式。如果文件存在,写入的内容会追加到文件末尾;如果文件不存在,会创建新文件。读取时从文件开头开始,写入时从文件末尾开始。 |
综合案例
案例:


bill.txt
name,date,money,type,remarks
周杰轮,2022-01-01,100000,消费,正式
周杰轮,2022-01-02,300000,收入,正式
周杰轮,2022-01-03,100000,消费,测试
林俊节,2022-01-01,300000,收入,正式
林俊节,2022-01-02,100000,消费,测试
林俊节,2022-01-03,100000,消费,正式
林俊节,2022-01-04,100000,消费,测试
林俊节,2022-01-05,500000,收入,正式
张学油,2022-01-01,100000,消费,正式
张学油,2022-01-02,500000,收入,正式
张学油,2022-01-03,900000,收入,测试
王力鸿,2022-01-01,500000,消费,正式
王力鸿,2022-01-02,300000,消费,测试
王力鸿,2022-01-03,950000,收入,正式
刘德滑,2022-01-01,300000,消费,测试
刘德滑,2022-01-02,100000,消费,正式
刘德滑,2022-01-03,300000,消费,正式
代码:
【自构案例】银行系统的进一步完善
在前面学习了容器类型——列表后,我初步对银行系统进行了完善,当时虽然增加了注册、修改密码、注销账户等功能,但奈何每次运行程序时,上一次的修改内容都无法保存。在学习了本节的内容以后,终于可以将用户信息进行保存了。
异常、模块和包
什么是异常?
当检测到一个错误时,Python 解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的“异常”, 也就是我们常说的 BUG。bug 在英语中有 虫子 的意思,将 bug 称之为异常有着历史原因。
如何捕获异常(异常处理)?
世界上没有完美的程序,任何程序在运行的过程中,都有可能出现:异常,也就是出现 bug 导致程序无法完美运行下去。我们要做的,不是力求程序完美运行。而是在力所能及的范围内,对可能出现的bug,进行提前准备、提前处理。捕获异常的作用在于:提前假设某处会出现异常,做好提前准备,当真的出现异常的时候,可以有后续手段。
基本语法:
try except else finally
使用 try….except… 句式可以捕获全部异常,但无法保留系统的报错信息。

捕获全部异常并保留报错信息的语法:
except Exception as e
注意与基本语法区分。

捕获特定异常的语法:

捕获多个异常的语法:

捕获异常并输出系统的报错信息:

加入 else 和 finally 语句的捕获异常语法:

异常的传递性
异常的传递性表现在相互嵌套中,如函数的相互嵌套。
在下面的例子中就发生了异常的传递:

当函数 func01 中发生异常,并且没有捕获处理这个异常的时候,异常会传递到函数 func02,当 func02 也没有捕获处理这个异常的时候, main 函数会捕获这个异常,这就是异常的传递性。
模块的概念及导入
模块其实就是一个 python 文件,和我们自己写的 python 文件一样,模块中含有别人预先写好的类、函数和变量。
模块的好处在于,当我们想要设计某个项目时,由于时间上的限制,一些功能比较复杂的函数没有时间和精力去编写,如果网络中别人已经写好了这个函数,我们就可以将他写好的模块下载下来导入到我们自己的项目中,从而提高自己的效率。
模块的导入方法:

上面语法中的中括号表示可选参数,即我们可写可不写,但是 import 是一定要写的。
模块的导入语句最好都写在代码文件的开头,这样会使代码更加整齐,不至于乱糟糟的。
模块导入方法的使用案例:







一般不会选择导入模块中的所有方法,因为这样做有一个风险:导入模块中的函数和变量可能会与自己代码中的函数或是变量重复,从而导致代码报错。
给导入的模块取别名主要有两个使用场景:
- 取一个自己使用起来方便的名字,避免导入的函数名与自己代码中的函数名冲突;
- 当导入多个模块时,有几个模块中的函数名、类名重复。
自定义模块('__main__'
的作用,两个下划线)
自定义模块的情景:当我们构建某个项目时,发现多个项目都需要相同的函数或类,我们就可以将这些函数和类另外放置在一个 python 文件中,这样就不用每个项目都写一次了。
事实上,每个 python 文件都可以作为一个 模块,模块名指的就是 python 文件的文件名,自定义模块的名称必须要符合标识符命名的规范。
在我们编写 python 代码,创建函数的时候,时常会去测试自己的函数写的是否正确,是否能够实现自己的功能。当我们将 python 文件作为模块调用的时候,如果没有将这些测试代码删去,就会对导入模块的代码造成影响,测试代码的运行结果会在导入模块的代码中出现。
这里一定一定要注意:
__name__
和 '__main__'
一样,单词两侧都是两个下划线,很多人会在这里吃亏。'__main__'
语句的介绍:只要当代码文件直接运行,而非作为导入的模块运行时,当前代码文件的名字属性
__name__
才会是字符串 '__main__'
。因此,可以通过
if __name__ = '__main__':
语句来判断当前代码是否是运行中的代码。'__all__'
(同样也是两个下划线)涵盖了某个 python 文件中所有的函数、类和变量,因此使用 from module import *
代码的时候,导入的是 '__all__'
中的所有内容。如果我们对
'__all__'
中的内容做出修改,那么使用 from module import *
语句的时候就不会导入 module.py 中的所有内容。如下图所示:

自定义包
包本质上是一个存放了许多.py
文件的文件夹,每一个.py
文件都可以作为模块导入。包与普通的文件夹的区别在于:包中必定含有一个__init__.py
文件,该文件用于控制模块的导入方式,里面可以什么都不写。包的使用场景:当我们有特别多需要导入的模块时,可以单独创建一个文件夹来管理这些模块,如果与主代码混在一起,不仅看的不舒服,也有可能会造成混乱。
导入包的几种方式:
方式一:

注意:这里可能很多人会直接写为
my_module1.info_print1()
,这样是不行的,前面一定还要引用 my_package
。前面介绍过模块可以通过
import 模块名
的方式导入,我测试了一下,包是无法通过 import 包名
的方式导入的,必须是 import 包名.模块名
。可以将
包名.模块名
看作是一种新的模块名,这样前面导入模块的 5 种方式就都可以使用了。当然,也可以给新的模块名取上别名。方式二:
方式三:
方式四(取别名):
方式五(只对 
from package import *
语法生效):
第三方包的安装

想要下载第三方的包,主要有两种方式:
- 通过命令行提示符下载。
按下Win+R
输入 cmd 回车打开命令行提示符,在里面输入pip install [包名]
即可安装相应的包,如pip install numpy
。如果你拥有“科学上网”的能力,那么你的下载速度应该比较快;如果你没有,那么就需要利用清华镜像站去下载,使用pip install -i https://pypi.tuna.tsinghua.edu.cn/simple [包名称]
即可。
如果你安装了 anaconda,那么请通过这个方式安装
首先,按下
Win+R
输入 cmd 打开命令行窗口,通过 conda activate [你的环境名称]
(假设你先前已经创建过环境了)的方式进入虚拟环境,然后输入 conda install [包的名称]
的方式即可下载包了,比如 conda install numpy
。如果你之前没有创建过环境,那么请你先通过指令
conda create -n [环境名称] python=3.x
的方式创建环境,比如 conda create -n py_learn python=3.x
,然后通过 conda activate py_learn
即可打开。- 在 pycharm 中进行下载,我是可以正常进行下载的,不过有些弹幕说需要能够上外网才行,可以自己试试。

综合案例
案例:

代码:
数据可视化(绘图,pyecharts)
什么是 JSON 格式?
JSON 是一种轻量级的数据交互格式,可以按照 JSON 指定的格式去组织和封装数据。
JSON 本质上是一个带有特定格式的字符串。
JSON 就是一种在各个编程语言中流通的数据格式,负责不同编程语言中的数据传递和交互。
各种编程语言存储数据的容器不尽相同,在Python中有字典dict这样的数据类型, 而其它语言可能没有对应的字典。为了让不同的语言都能够相互通用的互相传递数据,JSON 就是一种非常良好的中转数据格式。

JSON 格式的书写方法
JSON 格式其实与 python 中的字典是相似的,只不过它本质上是一个字符串。

JSON 数据与 PYTHON 代码的相互转换
JSON 可以与 PYHTON 的字典或列表无缝切换。

注意:当需要转换为 json 的 python 字典中含有中文时,需要使用
json.dumps(data, ensure_ascii=False)
语句,即加上关键字参数 ensure_ascii=False
。Pyecharts 实现数据可视化
Echarts 是个由百度开源的数据可视化,凭借着良好的交互性,精巧的图表设计,得到了众多开发者的认可。 而 Python 是门富有表达力的语言,很适合用于数据处理。当数据分析遇上数据可视化时 pyecharts 诞生了。
Pyecharts 的安装
首先按下快捷键
Win+R
,在弹出的窗口中输入 cmd 打开命令行提示符。打开命令行提示符后,输入
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyecharts
即可实现安装。更多有关 Pyecharts 的内容可以访问: https://gallery.pyecharts.org/#/README 。
Pyecharts 生成折线图
基础使用
本部分只介绍图像最基本的横坐标、纵坐标的使用。

add_yaxis 的相关方法:

如何让生成的图表显示出来呢?

有一个小小的技巧,只有第一次打开 render.html 时需要通过上面的方式,后续如果修改代码重新运行,只需将打开的网页刷新即可,不用再关闭重新打开。
【配置选项】丰富图像内容
最常用的几个配置选项如下图所示,其中如 TitleOpts 这样的字母表示需要提前导入的模块。


案例
下面三个文档文件为疫情期间三个国家的新冠确诊人数、治愈人数、死亡人数和新增病例,使用 JSON 格式保存。
将上述 txt 文件中的数据绘制成折线图,横坐标是时间,纵坐标是具体人数。
为了对上述 txt 文件中数据的层级有更深刻的了解,我们可以利用 JSON 数据格式化网站(老师上课使用的网站:JSON在线视图查看器)来梳理数据的层级结构。
层级结构梳理过程
在导入 txt 中的内容到网站前,先要确认 txt 中的数据是否完全符合 JSON 格式。我们的 三份 文件是不符合这个格式的,因为开头和结尾部分多出了一些无用的字符,需要先将它们删除。
开头需要删除的字符(蓝框选中部分):

结尾需要删除的字符(蓝框选中部分):

将删除无用字符后的 JSON 数据复制到 网站 中,点击格式化。格式化后在“视图”选项卡下就可以查看 JSON 的层级结构了。

在“视图”选项卡下 JSON 每个层级的详细解释:


梳理完数据的层级结构后,就可以开始编写代码来处理数据了。
- 首先要对数据进行整理,去除无用字符;
- 将 JSON 格式的数据转换为 python 数据;
- 取出其中的日期数据和病例数据(这一步需要对照网站中的 JSON 层级结构来梳理);
- 通过前面学习的折线图将数据用折线图绘制出来。
有没有觉得上面的代码特别冗余,很多重复的地方,因此又可以使用函数进行操作啦!下面是使用函数操作的代码
Pyecharts 生成地图
生成地图的方法与生成折线图的方法大同小异。
基础的地图
地图的进一步完善:
全国疫情地图
要生成全国疫情的地图,我的思路是这样的:
- 将文件打开,将 json 数据提取到变量中;
- 将 json 数据转换为 python 数据;
- 将文件中的 json 数据复制到 json 格式化网站 中,提取出其层级结构;
- 根据层级结构写代码,提取出我们需要的数据(全国的或是某个省份的);
- 通过 for 循环将数据合并为 map 能够处理的元组;
- 生成地图。
看过老师的操作后,你可能会选择使用下面的代码:
但请仔细观察 txt 文件中的 JSON 数据,其中的每个市和省份的最后都是没有加 “市/省” 的,因此,我们需要对代码作出进一步的修改。
下面,我们需要根据数据点的大小来给每个省份赋上不一样的颜色。
河南省疫情地图
生成河南省疫情地图的思路与全国疫情地图的思路相同:
- 将文件打开,将 json 数据提取到变量中;
- 将 json 数据转换为 python 数据;
- 将文件中的 json 数据复制到 json 格式化网站 中,提取出其层级结构;
- 根据层级结构写代码,提取出我们需要的数据(全国的或是某个省份的);
- 通过 for 循环将数据合并为 map 能够处理的元组;
- 生成地图。
Pyecharts 生成柱状图
基础柱状图的生成
时间线配合柱状图
时间线其实也可以理解为是一种包含横纵坐标的图表,只不过它的横坐标是时间,纵坐标是图像。
时间线给我的感觉是图像的嵌套,如前面我们学过的很多嵌套一样——循环的嵌套、函数的嵌套等等,时间线是纵坐标里嵌套了柱状图,而柱状图又有自己的横纵坐标。
时间线的横坐标如果是手动控制的,就会产生类似 ppt 翻页 的效果;如果是自动播放的,在速度足够快时,就会产生类似于 视频播放 的效果。

时间线配合折线图
时间线的自动播放功能
需要利用这行代码:
timeline.add_schema()
时间线主题颜色设置

根据上面表格中的颜色编号来设置颜色。具体操作方法如下面的代码所示:
嵌套列表的排序方法
【回忆】.sort()
方法和 sorted
函数
主要区别:
sorted()
是内置函数,而.sort()
是内置方法,内置函数和内置方法的调用方式不同;
内置函数:sorted(排序对象)
; 内置方法:排序对象.sort()
sorted()
不能修改原操作对象,而是会将排序后的列表返回给一个新的对象;.sort()
直接对原操作对象进行修改。
基于第二条的特性,引入第三条区别。
- 由于
.sort()
直接对原操作对象进行修改,因此.sort()
只能对列表进行操作;而sorted()
是返回给一个新列表,因此对于五种容器类型都可以进行排序,即既可以对列表进行操作,也可以对元组、字典进行操作。
值得注意的一点是,sorted 也可以对字符串和集合进行排序,它可以将字符串和集合中的每个字符提取出来进行排序,并返回给一个列表。
相同点:
都可以对列表进行排序,排序后的结果都是列表。
嵌套列表的排序方法
对于嵌套列表而言,可以使用匿名函数的方式来进行排序。
【综合案例】动态柱状图的构建
ANSI 格式的文件
ANSI 格式指的是根据电脑的操作系统来显示编码格式,如果是中文的电脑,编码格式为 “GB2312” 。
案例
案例:
现在有一份 1960~2019 年全球 GDP 数据的 csv 文件,请将文件中的内容做成自动播放的动态柱状图。要求如下:
- 每个年份对应一个柱状图,柱状图的横坐标为国家,纵坐标为 GDP 数据,并将横纵坐标调换;
- 柱状图中 GDP 总量最高的国家排在最上方,GDP 总量最低的国家排在最下方,每个柱状图仅显示 8 个国家;
- 柱状图的主题设置为
ThemeType.LIGHT
,每个柱状图的标题设置为 “[年份] 全球前 8 GDP 数据”。
代码:
类和对象
什么是类?
类的一个作用就是帮助我们组织和管理数据,让重复性的内容具有相同的基本属性。
比如,如果我们有一个学生类,那么他们需要的共同的基本属性就是姓名、年龄、籍贯等等,为了避免有些学生多写属性,有些学生少写属性,也为了便于数据的管理,我们可以在学生类中创建很多基本属性(类的变量)。
又比如,现在我们要对某些动物(如马、牛、羊)的行为进行研究,它们都会走路、吃饭、跑步,于是我们可以创建一个动物类来进行管理。在动物类中,可以创建一个类的变量来存储动物的名称,创建 run()、walk()、eat() 这些方法来表示他们共通的行为,这样,未来我要添加新的动物时,它默认就会有走路、吃饭、跑步这些属性,不用我们重复添加。
关于类的好处,需要在实践中慢慢体会。先简单从类的变量开始理解:
类的定义和使用
类的定义语法如下:

类外面定义的称为函数,类里面定义的称为方法。
一般在定义类的时候,类名称的首字母都是大写的。
方法的定义方式与函数的定义方式有几点差别:
- 方法在定义时参数列表中需要强制带上
self
参数;
- 在方法中使用到类变量的时候需要写成 self.[类变量] 的形式
方法的使用方式与函数的使用方法也有差别:
函数直接通过
函数名(参数列表)
的方式进行调用,而方法是通过 对象.方法()
的方式进行调用的;方法在类外调用的时候无需写
self
参数,python 内部会自动帮我们调用。【类的理解】程序中的类和现实世界中的类的联系
在程序中,类具有属性和行为两种特性;而在现实世界中,事物往往也可以归类出属性和行为两种特性。因此它们可以进行类比。

这样,如果我们要使用 python 的类组织和管理现实世界中的事物,我们就可以根据上面的示例先对事物的属性和行为进行归纳,然后再在程序中进行编写。
那么为什么类一定要创建对象才能够使用呢?
我们可以把类看作是一个“设计图纸”,根据设计图纸我们就可以生产出很多实体(对象),这样才有实际意义,这些对象所具有的基本属性都是相同的。
通过类创建对象的编程方法,业内称之为“面向对象编程”。
__init__()
方法(两个下划线)

在前面构造类的过程中,我们会发现给类的变量赋值比较麻烦,因为要写很多行的代码,而 python 的优雅之处就在于,在你觉得代码繁琐时,总会有简洁的方式帮助你快速解决问题。因此,对于这些类的变量,也有快速初始化的方法——就是构造
__init__()
方法。注意,
__init__()
方法也是左右两个下划线,同时也不要忘记写上默认参数 self
。使用
__init__()
方法初始化的类代码如下:
在创建对象的时候,直接在类名后面的括号中进行传参,传入的参数会自动进入
__init__()
方法中,与过去所学的函数参数传递一样。【小结】案例
案例:

案例思考:
对于上面这个案例,当时在写的时候就一直在想,这个案例是否有类都能够完成,有类反而增加了代码量,既然这个案例是要求用类完成的,那么必然要创建对象,因为类是要与对象绑定在一起的,这样用类完成案例才有意义。我当然可以在类外面就创建 10 个对象,但这肯定不符合现实的要求,因为现实中不可能只需要记录 10 个学生的信息,如果有百个,千个学生呢?因此,对象需要在循环中进行创建。
那么,在循环中加入一条创建对象的语句?类似于
stu = Student(name, age, addr)
,评论区有一个人就是使用这种方法完成的案例。评论区代码
但我认真思考后,否定了这种方法,因为这样创建的对象,有跟没有是一样的,我同样可以用没有类的代码来实现。
无类的代码实现
后来,我想到了一个方法,将对象作为字典的值传入字典中,这样,既能够在循环中创建 10 个对象,又能够录入每个对象的信息,只有这样,我认为才能够贴近这个案例的实现要求。
【上述思考仅个人薄见,如有更好的想法和代码,欢迎在评论区交流】
代码:
魔术方法
魔术方法是类中内置的方法的名称,我们可以重定义 python 中这些魔术方法,也叫重载。
常用的魔术方法
老师主要介绍了运算符魔术方法的修改。

__str__
字符串方法
当我们调用print(obj)
或str(obj)
函数的时候,会默认调用obj.__str__()
,即默认调用__str__()
方法。
__lt__
小于运算符
lt
顾名思义是less than
,小于,因此只会对"<"
符号进行重载。
__le__
小于等于运算符
le
顾名思义是less than or equal to
,小于等于,因此只会对"<="
符号进行重载。
__eq__
等于运算符
eq
顾名思义是equal to
,等于,因此只会对"=="
符号进行重载。
其他一些魔术方法
__len__(self)
- 作用:定义容器的长度,用于
len(obj)
。
- 代码示例:
2.
__getitem__(self, key)
- 作用:定义通过索引或键访问元素的行为,用于
obj[key]
。
- 代码示例:
3.
__setitem__(self, key, value)
- 作用:定义通过索引或键设置元素的行为,用于
obj[key] = value
。
- 代码示例:
4.
__delitem__(self, key)
- 作用:定义通过索引或键删除元素的行为,用于
del obj[key]
。
- 代码示例:
面向对象编程的三个属性
类具有三个属性:封装、继承和多态。
封装(私有成员的定义与使用)

在程序中,一些成员变量和属性是可供用户使用的,也就是能够被直接访问的,如上网、通话等等行为,称为是公有成员;
而有些属性是系统需要但不能够被用户访问的,如手机的驱动程序、运行电压等,称为是私有成员。
那么如何定义私有成员呢?

如何使用私有成员呢?

私有成员一般无法在类外面进行访问,否则会报错:

但是,类中的方法是可以访问私有成员的:

【封装】案例
案例:

代码:
继承
在我们的类更新换代的时候,如果需要在原有的类中添加内容,为避免与原有的类混合在一起导致混乱,就需要使用到继承的语法新建一个子类将父类的内容继承到新的子类中。
继承的具体作用:
从父类那里继承(复制)来成员变量和成员方法(不含私有)。
子类构建的类对象,可以
- 有自己的成员变量和成员方法;
- 使用父类的成员变量和成员方法。
继承分为两种情形—— 单继承 和 多继承:
单继承指的是只有一个父类需要继承,多继承指的是有多个父类需要继承。
继承的语法:

单继承:

括号中只需要加入 “phone” 这个类名即可。
多继承:


pass
语法:上图中,多继承的类 MYPhone
使用了 pass
作为类的内容,pass
表示什么都不做。由于 python 中要求函数和类的内部都需要填充代码,而有时候我们可能会遇到上图只使用多继承的情形或是只是想先放个函数或类而不着急填充代码避免自己后续忘记编写,此时就可以使用 pass
语句来避免 python 中出现语法报错。多继承时多个父类中出现同名成员:
多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右)为优先级。即先继承的保留,后继承的被覆盖。
多继承的代码演示如下:

【特殊情况】无继承情况下调用其他类的变量
【继承】复写(super()
方法)
代码复写指的是在子类中定义了与父类相同的成员,则子类成员会覆盖父类成员。
在类中,一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员。
如果需要使用被复写的父类的成员,需要特殊的调用方式(主要有两种):
- 使用父类名直接调用(方法括号中记得要加入
self
)
使用成员变量:
父类名.成员变量
;
使用成员方法:父类名.成员方法(self)
- 使用
super()
的方式(该方式无需在方法括号中写上self
)
使用成员变量:
super().成员变量
使用成员方法:super().成员方法()
代码示例如下:

多态
多态是在继承基础上的进一步延伸。
引入
多态,顾名思义,指的就是多种状态,表示同一种行为可以有多种表现形式。
可以通过一个例子来简单理解多态:大部分动物如狗、猫等等都有一个特性是会叫,但狗的叫声和猫的叫声不一样,也就是说同样都是“叫”这个行为,狗和猫的表现形式不一样。
多态的使用方法使用下面三句话来进行声明:
- 以父类做定义声明(空实现);
- 以子类做实际工作;
- 定义同一行为 → 获取不同状态。
抽象方法和抽象类
抽象方法:类中的方法体如果是空实现(即只有一行 pass 代码),则称为是抽象方法。
抽象类(接口):含有抽象方法的类称为是抽象类。
如何理解抽象类的作用呢?
抽象类可以想象成是一个标准,所有的子类都必须要符合这个标准才合格。
举一个现实生活中的例子:所有的空调都必须符合能够制冷、制热的标准,否则就是不合格的。
抽象类多用于做顶层设计(设计标准),以便子类做具体实现,是对子类的一种软性约束,要求子类必须复写(实现)父类的一些方法并配合多态使用,获得不同的工作状态。
【类和对象】综合案例
案例:
某公司,有2份数据文件,现需要对其进行分析处理,计算每日的销售额并以柱状图表的形式进行展示。
【第一份文件为 csv 格式的数据文件,第二份文件为 json 格式的数据文件】
代码:
我的代码
“老师的代码”
下载的资料里面似乎没有这个案例的代码,下面的代码是我按照老师的思路写的,不和老师的完全一样,但是大体上是相同的。
我的代码文件是在 python 3.9 版本环境下运行的,为了避免文件打不开,将各个文件中的代码也附在下面。
main.py
file_define.py
data_define.py
与老师代码对比后的思考
思考 1 :
在我的代码中,并没有定义抽象类,原因是我对于这个案例的理解不如老师的深刻。前面我们学习过,抽象类用于定义一个标准,多态是同一种行为的多种实现方式。
在这个案例中,两份文件的表头都是一样的(
date
,order_id
,money
,province
),也就是说它们是符合同一个标准的,只要看出了这一点,我们就会想到用抽象类来实现案例。思考 2:
可以将数据的表头作为父类的成员变量,将不同数据的类型(csv 格式,json 格式)作为子类,将子类的各个数据按照父类的标准进行存放,数据的每一行作为一个对象,将这些对象放入列表中。每个对象都具有
date
,order_id
,money
,province
四个属性,将对象作为一个元素存放到列表中相当于是将四个成员变量作为一个元素放入列表中。思考 3:
在未来要设计项目的时候,应该拥有这样的思路,先构造一个抽象类用作顶层设计,确定需要实现哪些功能,然后再根据子类的特性设计子类的成员方法和成员变量。
在综合案例中,老师将文件的读取行为和表头的四个属性分为两个类来定义,
Python 中有趣的内置函数/方法
随机数函数
在 Python 中,
random
模块(需要在代码开头加上 import random
)提供了多种生成随机数的函数,适用于不同的场景(如生成随机整数、浮点数、序列操作等)。以下是常用的随机数函数及其用法:1.
random.random()
- 功能:生成一个
[0.0, 1.0)
范围内的随机浮点数。
- 示例:
2.
random.randint(a, b)
- 功能:生成一个
[a, b]
范围内的随机整数(包含两端)。
- 示例:
3.
random.uniform(a, b)
- 功能:生成一个
[a, b]
范围内的随机浮点数。
- 示例:
4.
random.choice(seq)
- 功能:从非空序列
seq
中随机选择一个元素。
- 示例:
5.
random.sample(seq, k)
- 功能:从序列
seq
中随机选择k
个不重复的元素。
- 示例:
6.
random.shuffle(seq)
- 功能:将序列
seq
原地随机打乱顺序(直接修改原序列)。
- 示例:
7.
random.seed(a=None)
- 功能:初始化随机数生成器的种子值,保证生成的随机数可复现。
- 示例:
8.
random.gauss(mu, sigma)
- 功能:生成符合高斯分布(正态分布)的随机浮点数,均值为
mu
,标准差为sigma
。
- 示例:
9.
random.randrange(start, stop, step)
- 功能:生成
[start, stop)
范围内按step
步长的随机整数。
- 示例:
10.
random.choices(population, weights=None, k=1)
- 功能:从
population
序列中有放回地随机抽取k
个元素(允许重复)。
- 示例:
延时函数
响铃函数
进阶语法
📝 Class Notes
这部分进阶的内容是为了让 python 的代码更加简洁高效,在我的理解中,代码越是简洁高效,就越是优雅。
【补充】local
、global
和 nonlocal
关键字
关键字 | 作用域 | 用途 | 是否需要显式声明 | 典型场景 |
local | 函数内部 | 默认情况下,函数内部定义的变量是局部变量( local )。 | 否(默认) | 函数内部的临时变量 |
global | 全局(模块级) | 在函数内部声明并修改全局变量。 | 是 | 跨多个函数或模块共享变量 |
nonlocal | 外层嵌套函数(非全局) | 在嵌套函数内部声明并修改外层函数的变量(非全局)。 | 是 | 闭包中修改外层函数的变量 |
在 Python 中,当内部函数仅修改外部函数中可变对象(如列表、字典等)的内容时,不需要使用
nonlocal
声明。- 读取外部变量:内部函数可以直接读取外部函数的变量。
- 修改变量指向的对象:若内部函数尝试对 外部函数变量 重新赋值(如
res = [1,2,3]
),则需要nonlocal
声明,否则 Python 会将其视为局部变量。
- 修改对象内容:对于列表、字典等可变对象,通过方法(如
append
、pop
)或索引修改其内容时,不需要nonlocal
。
闭包
当我们希望一个函数既能够访问全局变量,又不希望全局变量轻易发生更改时,就可以使用到我们本节所学的闭包。
闭包有点类似于 C++ 里面的static
关键字定义的静态变量。
为了更好的理解闭包,可以先看看下面 atm 机存款 的例子:

在上面的代码示例中,我们的需求是创建一个函数实现存取款的操作,同时又不希望我们的余额在调用
atm()
函数以外的操作中发生修改,为此是可以使用闭包的。
要实现闭包的功能,需要满足两点要求:
- 外层函数嵌套一个内层函数,且外层函数的返回值为内层函数的函数名(不要加括号);
- 内层函数内部调用外层函数中的变量。在使用外层函数变量前,最好在内层函数中通过
nonlocal
关键字声明外层函数要修改的变量,否则在某些情形下会报错。
闭包的优缺点:
优点:
- 无需定义全局变量即可通过函数持续地访问、修改某个值;
- 闭包使用变量的作用域在函数内,难以被错误的调用修改。
缺点:
- 由于内部函数持续引用外部函数的值,所以会导致这一部分内存空间不被释放,一直占用内存
【但其实单个变量所占有的内存空间一般都不大,因此这个缺点与优点比起来微不足道】
装饰器
装饰器是在闭包的基础上实现的。它完成的工作是:在先前创建的闭包函数内调用目标函数,可以达到不改动目标函数的同时,增加额外的功能。
刚接触装饰器,我想到的一个使用场景就是为闭源函数或不允许发生改动的函数新增我们想要的功能,由于装饰器可以在不改动目标函数的同时增加额外功能,因此用于这个场景再合适不过了。
那么,装饰器该如何实现呢?
第一种实现方式 —— 高级但不优雅:

第二种实现方式 —— 语法糖(优雅且高级):
语法糖:对程序本身的运行没有什么影响,但对于程序员来说,有了这个语法就可以使得程序的可读性、易用性增强。

使用
@outer
语法糖其实完成的功能与第一种实现方式没什么区别,但程序却会变得更加高级。设计模式的简单介绍
设计模式是一种编程套路,可以极大的方便程序的开发。
设计模式引入
设计模式并非是引入什么新的语法,个人认为应该是一种设计的经验(编程中的既定套路),学习这些设计模式可以提高代码运行的效率。
最常见、最经典的设计模式,就是我们所学习的面向对象了。除了面向对象外,还有以下这些设计模式:
- 单例、工厂模式
- 建造者、责任链、状态、备忘录、解释器、访问者、观察者、中介、模板、代理模式等
本部分内容只介绍 单例模式 和 工厂模式 。
单例模式
- 定义:保证一个类只有一个实例,并提供一个访问它的全局访问点;
- 适用场景:当一个类只能有一个实例,而客户可以从一个众所周知的访问点访问它时。
- 目的:节省创建类对象的开销及内存开销。
在正常情况下,如果我们要使用一个类创建对象实现不同的功能,可能会使用如下代码:
但左侧代码实际上可以仅用一个对象来实现:
很容易能够想到,上述代码的内存花销是会比左侧的代码低的,而实现的功能却是相同的。
这就是单例模式的初衷:仅使用一个实例对象实现相同功能以节省创建类对象的开销和内存开销。
那么,如果我们在多个 python 文件中都要使用到一个相同的类时,如何实现多个文件共用同一个实例对象呢?
答案就是将这个类单独放入一个文件中并提前创建好一个实例对象,其他 python 文件需要调用该类时直接导入创建好的实例对象即可。
工厂模式
当需要大量创建一个或多个类的实例的时候, 可以使用工厂模式。即从原生的使用类的构造去创建对象的形式迁移到基于工厂提供的方法去创建对象的形式。
优点:
- 大批量创建对象的时候有统一的入口,易于代码维护;
- 当发生修改,仅修改工厂类的创建方法即可;
- 符合现实世界的模式,即由工厂来制作产品(对象)。
非工厂模式的代码示例:
工厂模式的代码示例:
此时,如果我需要对上面的代码进行修改,即将所有的学生类对象修改为教师类对象,所有的教师类对象修改为学生类对象。
如果采用左侧的代码,修改起来的成本高,一个个更改的方案应该没有人使用,比较可靠的思路是使用
Ctrl+R
进行搜索替换,但由于原来就有学生类和教师类混合在一起,因此也只能一个一个仔细查看并替换;但如果采用右侧的代码,我们只需要将 if 条件判断语句进行小小的更改即可满足要求,修改后的代码如下:
现实生活中可能不会出现将学生类和老师类互换的情形,但可能会出现将两个类名互换的行为。
进程和线程,并行执行和并发执行
线程和进程引入:
现代操作系统比如 Mac OS X,UNIX,Linux,Windows 等,都是支持“多任务”的操作系统。
将一个操作系统看作是国家,操作系统内的各个进程看作是一家家公司,那么线称就是公司内部的一个个员工,这就是进程、线程的关系。
线程归属于进程,一个进程可以开启多个线程,执行不同的工作,是进程的实际工作最小单位。每个软件在操作系统中都是一个进程,软件内部的工作是多线程执行的。
内存隔离和内存共享:
进程之间是内存隔离的, 即不同的进程拥有各自的内存空间。 这就类似于不同的公司拥有不同的办公场所。
线程之间是内存共享的,线程是属于进程的,一个进程内的多个线程之间是共享这个进程所拥有的内存空间的。这就好比,公司员工之间是共享公司的办公场所。

并行执行和并发执行:
并行执行的意思是同一时间做不同的工作(需要多核 CPU 的支持)。
并发执行指多个任务交替执行(单核 CPU 也可实现,通过时间片轮转)。
进程之间就是并行执行的,操作系统可以同时运行好多程序,这些程序都是在并行执行。除了进程外,多核 CPU 的线程其实也是可以并行执行的(多线程并行执行)。
而对于 python 程序而言,由于 全局解释器锁(GIL)【GIL 的设计初衷是简化实现和保证线程安全】 的存在,多线程无法实现真正的并行执行,只能够实现并发执行,即多个线程之间是交替执行的。
python 代码的并发执行
python 中由于 GIL 的存在,多线程时只能够并发执行代码,即各个线程之间是交替执行的。
并发执行需要利用到
threading
模块。
注意,
.Thread
方法的第一个参数为 group
,是无用的参数,因此我们要传参时都要使用关键字传参。代码示例:
在上述代码,实践发现 python 中并发执行必须要在无限循环的情况下才可以进行,有限次数的循环难以实现,并不像弹幕中所说的仅仅加个次数限制就行。
要想实现次数的控制,需要利用到
threading
中的 Lock()
,调用 Lock()
的两个方法 .acquire()
和 .release()
才行。.acquire()
方法可以让某个进程处于阻塞状态, .release()
方法可以让某个处于阻塞状态的进程重新运行,通过前面两句的描述很多人应该就能够想到要如何进行操作了。次数控制的具体实现方式如下:
Socket 服务端和 Socket 客户端(设备通信)
Socket 引入
Socket负责进程之间的网络数据传输,好比数据的搬运工。系统中的两个进程如果想要实现通信需要利用 Socket。

2 个进程之间通过Socket进行相互通讯,就必须有服务端和客户端。
Socket 服务端:等待其它进程的连接、可接收发来的消息、可以回复消息;
Socket 客户端:主动连接服务端、可以发送消息、可以接收回复。

Socket 服务端开发
Socket 服务端开发步骤如下:

代码示例:
出现报错:“由于目标计算机积极拒绝,无法连接”
出现这种情况是因为先进行了客户端的连接,应该先运行服务端的代码,再连接客户端。

Socket 客户端开发
Socket 客户端开发与服务端开发步骤大体上一致,具体步骤如下:

代码示例:
当我们把客户端的代码写好后,实际上就可以进行本机的相互通讯了。我们先打开前面服务端的代码,运行,然后再打开客户端的代码运行,两个代码之间就可以实现交互了。
正则表达式
正则表达式,又称规则表达式(Regular Expression),是使用单个字符串来描述、匹配某个句法规则的字符串,常被用来检索、替换那些符合某个模式(规则)的文本。
简单来说,正则表达式就是使用:字符串定义规则,并通过规则去验证字符串是否匹配。
正则表达式的三个基础方法
使用三个基础方法前,需要通过import re
导入re
模块。
re.match()
:
.span()
方法和 .group()
方法分别返回匹配项的首尾位置(和切片首尾类似,比如 (0, 6)
表示首字符出现在第 0
个,尾字符出现在 6-1
个,即 5
个)和匹配的字符。这两个方法对于
.match()
和 .search()
方法返回的结果都是适用的。re.search()
:
re.findall()
:
元字符匹配规则
在写元字符匹配规则的时候,一定一定不要随意的加上空格,否则会导致功能无法实现。
如
^[0-9]{5, 11}$
这样就是错误的,因为 逗号 后面加上了空格。单字符匹配

上述通配符中,除了
[]
以外,其他的都比较好理解,[]
中列举的字符既可以是特定的几个字符,也可以是范围。特定的几个字符:
[! @]
表示筛选出 !
,
(空格),@
这三个字符,相互之间必须紧挨着,不能留空,留空表示筛选空格。范围的筛选方法:
如下面的几种使用方法都是可以的:
[a-z]
表示所有的小写字母;[A-Z]
表示所有的大写字母;[0-9]
表示所有的数字;前面三条表示范围的形式都可以自由组合,如:
[a-zA-Z0-9]
表示所有的字母和数字;特殊:
[A-z]
表示所有的字母(无论大小写)【但注意,[a-Z]
是错误的】;数量匹配

边界匹配和分组匹配

案例
- 匹配账号,只能由字母和数字组成,长度限制6到10位
规则为:
^[0-9a-zA-Z]{6,10}$
- 匹配QQ号,要求纯数字,长度 5-11,第一位不为0
规则为:
^[1-9][0-9]{4,10}$
- 只允许qq、163、gmail这三种邮箱地址(
{内容}.{内容}.{内容}.{内容}.{内容}.{内容}.{内容}.{内容}@{内容}.{内容}.{内容}
)
规则为:
r'^[\w-]+(\.[\w-]+)*@(qq|163|gmail)(\.[\w-]+)+$'
注意,由于这段通配符中含有转义字符 “\
”,因此前面需要加上 r
表示取消转义。对于正则表达式部分的内容,我们需要理解每个匹配的规则及用法即可,当给我们一个正则表达式,我们能够明白它的作用就算可以了。
如果要写正则表达式,我们一方面可以上网搜寻很多成熟的写法,另一方面也可以直接咨询 ai,我们只需要学会判断真伪就行。
递归
os
模块
在我们后面的案例中,需要利用到 os 模块的三个方法,这里简单介绍一下。
os.listdir
,列出指定目录下的内容;
os.path.isdir
,判断给定路径是否是文件夹,是返回 True,否返回 False;
os.path.exists
,判断给定路径是否存在,存在返回 True,否则返回 False。
案例
SQL
后面的章节中老师的案例代码似乎都没有上传了,不过不用担心,我将自己学习过程中写的案例代码以及按照老师思路写的代码都放在了案例中供大家参考,代码在 python 3.9 的环境下是都可以运行的。
另外,后面的 SQL 及 PySpark 我使用的都是老师提供的资料里面的安装包进行安装的,安装基本上都成功了,但弹幕中不少人安装失败,因此建议大家使用老师提供的安装包并一步步按照老师视频中的操作步骤做,这样成功率应该高一些【建议打开弹幕】。课程的资料和安装包我都放到这个页面里了,下载速度会比百度网盘零氪快很多。
📝 Class Notes
SQL 用于管理和存储数据,不管是何种开发语言,亦或是何种开发方向,SQL都是开发人员无法绕开的话题。除了一门趁手的编程语言外,SQL语言也是开发人员人人必备的开发技能。本门课程仅介绍 SQL 比较基础的使用,用于完成后续课程,更详细的课程链接:https://www.bilibili.com/video/BV1iF411z7Pu。
SQL 的背景
SQL全称: Structured Query Language,结构化查询语言,用于访问和处理数据库的标准的计算机语言。
SQL 语言于 1974 年由 Boyce 和 Chamberlin 提出,并首先在 IBM 公司研制的关系数据库系统 SystemR 上实现。
经过多年发展,SQL以成为数据库领域统一的数据操作标准语言,可以说几乎市面上所有的数据库系统都支持使用SQL语言来操作。
SQL 的引入
类似于 excel 表格,SQL语言,就是一种对数据库、数据进行操作、管理、查询的工具。
在生活中,excel 以如下方式存储数据:

类似的,SQL 语言以如下的方式存储数据:

如上图所示,SQL 将数据的存储分为三个层级——库、表、数据。一个库中有许多表,每个表中都存储着很多的数据。
我们所要做的,就是使用数据库软件去获得“库->表->数据”,并借助SQL语言,完成对数据的增删改查等操作。
常见的数据库软件有:Oracle、MySQL、SQL Server、PostgreSQL、SQLite
MySQL 的安装
MySQL 的背景
- MySQL数据库管理系统由瑞典的 DataKonsultAB 公司研发,该公司被 Sun 公司收购,现在 Sun 公司又被 Oracle 公司收购,因此MySQL目前属于 Oracle 旗下产品。
- MySQL 软件采用了双授权政策,分为社区版和商业版,由于其体积小、速度快、总体拥有成本低,一般开发都选择 MySQL 作为数据库。
- MySQL 还有一个集群版的版本,(弹幕中说:)集群就是多个服务器一起联合使用一个软件,MySQL 软件可以将多个服务器绑定一起使用。
下载方法
打开这个网址即可进入官网下载:https://downloads.mysql.com/archives/installer。在课程资料中老师也分享了这款软件的安装包。
安装过程在老师课程视频(视频链接)里有,需要注意的是打开安装包后,一定要选择
full
版本进行下载,不要选择老师视频中演示的 developer defalut
,否则可能无法使用,弹幕中也有很多错误的先例。按照老师的操作一步步来就可以安装成功了。如果有人不想要 MySQL 安装在 C 盘中,弹幕中说可以在开始的时候选择 custom 的模式,不过我也没有具体尝试过,想要更改位置的小伙伴可以探索一下。
MySQL 的简单使用
通过
Win+R
打开窗口输入 cmd
进入命令行提示符,在提示符中输入 mysql -uroot -p
会提示你输入密码,输入成功后即可进入数据库。在进入数据库后,我们可以使用以下这些指令:
show databases;
查看有哪些数据库(⚠️ 注意,show databases;
指令运行后如果报错,可以试试show databases ;
这条指令,即在分号前加上空格);
use 数据库名
使用某个数据库,如use world
;
show tables
查看数据库内有哪些表;
exit
退出 MySQL 的命令行环境。
DBeaver 的安装与使用
类似于 pycharm 是 python 的编辑环境,DBeaver 是 MySQL 的编辑环境,在 DBeaver 这个图形交互界面中可以方便地对数据库进行操作。
安装
在课程资料中就有 DBeaver 的安装包,DBeaver 的安装比 MySQL 的安装简单多了,按照老师视频中的操作(视频链接)做就行,这里不再赘述。
DBeaver 链接 MySQL
在打开 DBeaver 后,需要链接数据库,这里可能会出现一些问题。
首先需要新建数据库连接;

然后选择 MySQL 后点击下一步;

然后在页面中输入密码,点击测试连接;

在测试连接时,问题就出来了,很多人(包括我)都出现了
Public Key Retrieval is not allowed
这个问题,弹幕中很多人说可以通过【选“驱动属性”,找到“allowPublicKeyRetrieval”选项,把值改为“True”】 来解决,即
但我个人这么操作后却又报出了另外一个错误——
Access denied for user 'root'@'localhost' (using password: YES)
。我将软件关闭后重启,按照上面的操作再试了一次,测试连接后莫名其妙又成功了,这一次我并没有将
allowPublicKeyRetrieval
属性设置为 true
。所以我出现问题的原因可能是密码输入错误。大家如果出现这个问题可以试着重启软件重新输入一次密码,确认密码无误后再测试。DBeaver 的使用
在DBeaver 中如果要单独运行某行或某几行代码,需要鼠标选中,然后点击左侧的第一个黄色三角形【快捷键
Ctrl+Enter
】。如果要运行整体的代码,需要鼠标右键,点击 【执行→执行 SQL 脚本】,【快捷键
Alt+X
】。DBeaver 运行完代码后,需要刷新原来的数据库才会更新数据,比如,如果创建了一个表名叫
student
,那么就需要鼠标点击左侧 world
下的 “表” 选项,右键点击刷新(或者使用快捷键 F5
)。
SQL 的语法
分类

语法特征
前面两点与 python 不同,需要特别注意。
- 对大小写不敏感,如
USE WORLD
语句与use world
语句是一样的;
- 支持单行和多行书写,最后以分号 “
;
” 结束;
- 注释的形式:
- 单行注释:与 python 一样,可以使用
#
号进行注释,#
号后面最好加上空格;也可以使用--
(两个减号一个空格)进行注释; - 多行注释:使用
/*...*/
进行注释,将其中的省略号替换为注释的文本,印象中 C++ 好像也是这样多行注释的。
DDL 语法【库的管理、表的管理】
库的管理:
SHOW DATABASES;USE ;CREATE DATABASE;CHARSET;DROP DATABASE;SELECT DATABASE();

上述的
CREATE
代码中出现了中括号 [ ]
,它表示的是可选参数,在真正创建数据库的时候仅需要写上中括号中的内容即可,不要加上中括号。表的管理:
SHOW TABLES;DROP TABLE;DROP TABLE IF EXISTS;CREATE TABLE;int float varchar date timestamp

DML 语法【表中数据的管理】
主要有 插入、删除、更新 操作。
插入操作:
INSERT INTO

图中有两个注意点在这里再强调一下:
- 插入字符串数据的时候,不要用双引号包围,使用单引号(高版本的 DBeaver 可能可以正常运行)【字符串数据会显示绿色,如果你使用双引号,字符串变为了绿色说明是可以使用的】;
- 插入全部列的时候,可以省略表明后面的列1、列2等列数据。
下面再补充几点:
- 同时插入多列数据的时候,值的顺序要和前面列声明的顺序相同,比如前面是
student(id, name, age)
,那么后面值的顺序就要是(id, name, age)
;
- 如果是向全部列插入数据,列省略,那么值的顺序和定义的顺序是相同的。
- 表后面声明了多少列,值就需要写多少个,如果某一列不赋值,那么表后面就不要写那一列。
【简而言之就是值要和列一一对应,且不能是空的】
删除操作:
delete from … where

删除操作也有几个注意点:
- 删除操作中如果不加上
where
条件,就说明要删除表中的所有数据,即清空;
- 进行条件判断的时候,判断两个数相等只需要一个等号即可。
更新(替换)操作:
update … set … where

【DML】课后作业

DQL 语法【数据查询、分组聚合】
查询
数据查询:
select…from…

数据查询-过滤:

分组聚合(查询的更进一步)
分组聚合应用场景非常多,如:统计班级中,男生和女生的人数。
这种需求就需要:
- 按性别分组
- 统计每个组的人数
这就称之为:分组聚合。
分组聚合的语法如下:

上述语法可以理解为,先根据 group by 后面的列将原来的列表拆分为两个或多个部分,然后对各个部分的列表求和、求平均或是求数量。
代码演示:

这里再强调一次:只有对哪个列进行分组,哪个列才可以出现在 select 中(聚合函数除外),其他参量如果出现在前面,没有什么意义。如对性别分组,前面
select age…
,想想都知道是有问题的。排序分页(分组聚合的更进一步)
排序分页就是在分组聚合的基础上又增添了两条功能——排序和数目限制。
排序:

排序的结果都默认是降序的。
结果分页限制:

LIMIT
语句中,单写一个 n
表示只显示 n
行数据,如 limit 3
,只显示 3 条数据;如果
LIMIT
后面跟两个数据,那么表示从 第 n+1
条数据开始计,往后显示 m
条数据,如 limit 10, 3
表示从第 11
条数据开始计,往后显示 3
条数据。python 执行 SQL 语句操作 MySQL
要使用 python 执行 SQL 语句必须先导入pymysql
包,Win+R
→cmd
→pip install pymysql
。
基础使用(查询数据,执行 sql 语句)
创建到MySQL的数据库链接:
- 首先要导入模块;
- 其次获取数据库操作权限;
- (获取游标对象);
- 对数据库进行操作;
- 关闭数据库
上图中的
'localhost'
表示的就是自己的本地电脑。在写上面的代码时,一定要对好每个值是否正确,比如端口号是 3306,不要写成 3006 之类的,密码记得加上引号。我看到弹幕中有各种各样的问题出现,遇到报错的时候建议大家先确认自己的代码是否和上面一致,然后再上网搜寻。在执行查询性质的 SQL 语句和执行非查询性质的 SQL 语句的过程有些许不同,查询性质的语句要加上获取查询结果的代码。
非查询性质的代码:

查询性质的代码:

右侧的语句
cursor.fetchall()
就可以获取到所有的查询结果,查询结果需要使用元组进行存储,元组中的每个元素仍是一个元组,每个元素都代表了每一行的查询结果。如果使用
cursor.execute("insert into…")
向表中插入数据,运行代码后表中的数据仍然没有变化,这时候不要着急,因为还需要加上 conn.commit()
语句进行提交,后面会介绍。数据插入

在上图所示代码中,执行后是无法将数据插入到数据表 student 中的。
这是因为 pymysql 在执行数据插入或其它产生数据更改的 SQL 语句时,默认是需要提交更改的,即需要通过代码“确认”这种更改行为。通过链接对象
.commit()
即可确认此行为。但是,每次插入数据后都在后面写上
.commit()
会比较麻烦,因此我们可以考虑使用自动提交的方式,具体操作如下:
综合案例 1 (老师上课演示的案例)
首先,将之前类和对象的案例代码(
file_define.py
和 data_define.py
)搬移过来(main.py 需要作出修改):file_define.py
data_define.py
案例:
打开 DBeaver,新建一个库
py_sql
,在 py_sql
库中新建一个表 orders
,在表中添加四个列—— order_date
, order_id
, order_money
, order_province
。代码:
综合案例 2(作业)
案例:

代码:
学完后的思考
下面只是个人浅薄的见解,欢迎大家多多交流。
对于我而言,暂时并不需要使用多么高阶的 sql 语法。
对于目前学到的 sql 语法,想了一下,可以在以下场景中用到:
- 当有一份存有大量的数据的 csv 文件时,如果我的任务只是要研究前 n 个数据时,可以使用 sql 来快速提取;
- 当我想要对数据进行快速筛选(条件查询和过滤),比如我想要筛选出数据中所有男生的数据或是某个特定省份的数据时可以使用 sql;
- 当我想要取出数据中的某一列单独进行研究时,我可以使用 sql;
- 当我想要快速删除或更新表中的数据时,可以使用 sql。
在上述这些场景下,我觉得使用 sql 语法比起 python 代码来说会简便一些,不过如果真要处理表格数据,直接使用 excel 进行处理然后复制会不会更好一些(估计在少量数据时可以这么处理)?
如果代码需要全自动化执行,且有大量的表格数据需要处理时,使用 python 会比使用 excel 更加方便和快捷。
PySpark
Spark,全称 Apache Spark,是用于大规模数据(large-scala data)处理的统一(unified)分析引擎。简单来说,Spark是一款分布式的计算框架,用于调度成百上千的服务器集群,计算 TB、PB 乃至 EB 级别的海量数据。如果对大数据感兴趣,可以深入学习 PySpark 技术,它是大数据的核心技术。
注意:PySpark 是在计算 TB、PB 乃至 EB 以上的数据时具有更大的优势,但如果数据比较少(比如我们后面案例中使用的数据),代码运行速度就会比较慢。因为数据需要先上传到 Spark 服务器中然后计算结束后再返回,时间长主要是因为数据传输比较慢。
PySpark
Spark 对众多编程语言都支持,但 python 是目前 Spark 中应用最为广泛的语言。
PySpark是由 Spark 官方开发的Python语言第三方库;Python开发者可以使用 pip 程序快速地安装 PySpark 并像其它三方库那样直接使用。

PySpark 的安装
PySpark 仍然使用命令行提示符进行安装,在 cmd 中输入
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyspark
代码即可进行安装(我个人没有这样进行安装,因为我是 anaconda 配置的 python 环境,因此直接使用 conda install pyspark
进行安装了)安装完 PySpark 库后,按照老师的代码完完全全的输入到 pycharm 后,代码可能还是无法运行,出现下面的报错:
弹幕中很多人都出现了上述的报错,解决方法就是安装 JAVA 的 jdk。
我安装的 PySpark 版本是 5.3.7,需要安装的 JAVA jdk 版本是 JAVA 11【如果大家不知道自己的 PySpark 版本是什么,就无脑装 JAVA 11 即可,不要下载太高版本的 JAVA,可能会没有适配 PySpark】。JAVA 11 的安装地址:下载地址 。
在官网上下载需要注册 oracle 账户,如果大家不想注册也可以直接搜索 JAVA 11 安装包,应该会有人分享。
下载安装 JAVA11 后,一定要先重启电脑,然后再打开之前运行错误的程序重新运行,这次应该就可以成功运行了。
PySpark 进行数据操作的编程模型


创建 PySpark 环境的入口对象
刚接触 pyspark,很多语法都要死记硬背,初学时不需要了解这些语句为什么都是这样写的,等深入学习 pyspark 后自然会懂得具体含义。
SparkContext类对象,是PySpark编程中一切功能的入口。

读取 python 容器或外部文件 → RDD 对象
RDD 全称为:弹性分布式数据集(Resilient Distributed Datasets)。所有的数据都必须转换为 RDD 对象才能在 spark 中进行数据处理。
python 容器 → RDD 对象
将 python 容器转换为 RDD 对象需要利用到.parallelize()
方法;如果要打印输出 RDD 对象中转换的内容需要利用.collect()
方法。现在并不需要深究为什么要用.collect()
方法打印,后面会介绍。

python 中的字符串转换为 RDD 对象时,
.parallelize()
方法会将字符串中的每个字符都提取出来存放在列表中。python 中的字典转换为 RDD 对象时,只有键会被提取出来存放入列表中,值会被丢弃。
外部 txt 文件 → RDD 对象
读取外部文件时,使用的方法是.textFile()
方法;打印内容时同样使用.collect()
方法。
读取后的效果与.readlines()
方法是一样的,都是将文件的每一行作为一个字符串元素放入列表中

我还尝试了一下导入 csv 格式的文件,发现导入后数据会出现乱码现象,于是,我尝试将 csv 文件的后缀更改为 txt,虽然打开 txt 文件后显示没什么问题,但是输出后依然是乱码的。最后我新建了一份 txt 文本文件,使用了 csv 文件相同的名字,将 csv 使用记事本打开并将所有数据复制到新建的文本文件后,再次输出时所有字符显示正常。
因此,pyspark 无法直接处理 csv 格式的文件。
传入 RDD 的对象一般都是乱序的,顺序与输入的顺序相同是巧合。
RDD 算子(数据的处理)
RDD 的成员方法称之为是 算子。
在使用算子前,需要提前导入
os
模块,并加上这条语句 os.environ['PYSPARK_PYTHON'] = 'D:/【python 解释器路径】'
为 spark 选择python 解释器,否则代码会无法运行并报错。python 解释器路径如何查看
我是通过下面的方式查看的。


算子在使用时都需要定义一个新的变量来进行接收,它们不会对原来的 rdd 对象进行修改:
如
rdd = rdd.map(lambda a: a * 10)
,rdd_flatMap = rdd.flatMap(…)
,rdd = rdd.reduceByKey(…)
同时,需要记住 rdd.collect() 对象是不可以直接修改的,不能使用诸如 rdd.collect() = […] 这样的操作。
【回忆】lambda 匿名函数
lambda 匿名函数,顾名思义,就是没有函数名的函数,由于没有函数名,所以在后面的代码中无法通过函数名显式调用匿名函数,因此匿名函是临时函数,只能在定义的位置临时调用一次。
lambda 与 def 一样,都是定义函数的关键字。

在 lambda 函数的函数体如果是一个数学表达式(包括算术运算和比较运算),那么会作为计算结果会作为返回值返回。
map
算子 【列表元素循环调用函数】
用于将 rdd 数据中的每个元素都传入 func 中进行处理,并让返回值覆盖原来的元素。
完成后续案例后的反思:在使用 pyspark 库时,若代码中有遍历的操作,都要想一想能不能使用map
算子或flatMap
算子,可以节省代码量并提高效率。

map 算子需要传入一个函数,在文档中,传入函数的要求为
f:(T) → U
表示传入的函数需要能够接收一个参数并返回一个参数。map 算子可以理解为是将数据的遍历和函数调用合为一体。
下面,我会将使用 map 算子的代码与没有使用的代码进行对比,比如我们希望 rdd 中的每个数据都乘以 10:
如果不调用 map 算子,需要这样写:
如果我们直接使用 map 算子:
可以注意到,使用 map 算子的代码更为简洁。
由于
map
算子返回的结果仍然是 rdd 对象,因此 map 方法可以链式调用,如 rdd = rdd.map(func1).map(func2).map(func3) …
。
flatMap
算子 【循环调用函数+去嵌套】
flatMap
与 map
算子的用法一样,唯一不同的点在于会将结果的嵌套列表解嵌套为一层列表。 reduceByKey
算子 【分组聚合】
将远足中的内容先分组,再聚合。

学完 reduceByKey 这个算子,我突然联想到之前的很多案例都有日期固定,销售额累加这样的需求,过去我们都是使用字典来进行计算的,其实使用这个算子也能够解决。
但这个方法有两个缺陷:
- 加入 pyspark 库后代码运行速度太慢了,比起过去使用字典的方法,慢了太多;
- rdd.reduceByKey() 方法执行后,生成的 rdd 对象中的列表日期是乱序的,因此需要先对日期进行排序再绘制柱状图。
练习案例 1 —— 计算单词数量
第一种方法:利用
reduceByKey
算子老师的代码:
看完老师的代码,才知道自己对于 map(),flatMap() 算子的理解还不够深刻,每当代码中遇到需要遍历的操作时,都需要考虑一下是否用 map 算子或 flatMap 算子更加简洁和优雅,比起写循环用到的多行语句,算子的作用真的很强大。
第二种方法:利用 字典(速度最慢)
第三种方法:利用
pymysql
库filter
算子【过滤】
filter 顾名思义,就是对输入的数据进行过滤。

distinct
算子 【去重,无需传参】
![输出的结果为 [1, 3, 5, 6, 9]](https://www.notion.so/image/attachment%3Ad51fe88a-aa2c-4c32-9b05-310b81919db4%3Aimage.png?table=block&id=1a885c3d-c58c-80ef-a7ff-ff72b62a3c35&t=1a885c3d-c58c-80ef-a7ff-ff72b62a3c35)
sortBy
算子 【排序】

ascending
参数和 numPartitions
参数不写上也没有关系。sortBy 默认是升序排列。练习案例 2
代码:
我的代码除了变量名以外和老师的代码基本一致,因此就不再把老师的案例代码写一遍了。
【嗯,个人觉得这一部分的案例都会比较简单,但看弹幕中大家的反映似乎并不是如此,可能是我记了笔记的缘故。如果大家写这个案例的代码比较吃力的话可能是前面的几个算子没有很好的掌握,建议再多看看🤔。】
RDD 对象 → python 数据
collect()
算子
这个算子我们在之前的学习过程中就一直在用,其实它的作用就是将 RDD 对象转换为 python 的列表。

reduce()
算子
有点类似于前面学过的reduceByKey
算子,但不同点在于它不会进行分组,只会讲数据进行聚合,最终返回一个 python 数据。

take()
算子

count()
算子
类似于 python 中统计容器长度的len()
方法。

RDD 对象 → txt 文件
安装 Hadoop
在将 RDD 对象转换为 txt 文件前,我们需要先配置好
Hadoop
。调用保存文件的算子,需要配置
Hadoop
依赖- 下载Hadoop安装包
- 解压到电脑任意位置(最好是在英文文件夹下,文件的保存路径必须都是全英文的,文件名不要有中文,如
D:/Hadoop/hadoop-3.0.0
)
- 在Python代码中使用os模块配置:
os.environ['HADOOP_HOME'] = 'HADOOP解压文件夹路径'
- 下载
winutils.exe
,并放入Hadoop
解压文件夹的bin
目录内
- 下载
hadoop.dll
,并放入C:/Windows/System32
文件夹内
SaveAsTextFile
算子
输出的结果是一个文件夹,文件夹中存有输出的文件夹

当我们把 Hadoop 安装好后,就可以将 rdd 对象输出为 txt 文件了,代码示例如下:
在运行上面的代码时,无论是否成功运行,在下一次运行代码前都一定要把先前运行创建的文件夹删掉,比如这里就要删除
output
文件夹。上述代码成功运行后,应该会在 D 盘生成一个 output 文件夹,打开后,文件夹里面有如图所示的文件,其中红框部分框住的 16 个文件就是存放输出数据的文件,可以使用 记事本 打开。【黑马老师说,由于代码中没有设置分区数,因此数据是根据电脑的内核数量来设置分区数量的,我的电脑是 16 核,因此有 16 个文件】

设置电脑的分区数为 1 【输出的文件中就只有一个有数据】

上述代码的输出结果如下图所示:

综合案例
案例:

代码:
上述的 json 格式导出的代码有许多人都疑惑为什么没有做 json 的转换导出的数据就算是 json 呢?其实 json 格式的数据在 txt 文件中就是以 python 字典的形式储存的,此时花括号两侧没有引号。而将数据从 txt 文件中读取时,每个字典数据都会加上引号成为是字符串,如果要将该字符串转换成字典,无法通过 dict() 命令完成,需要通过
json.loads(data)
来完成。【Python 入门后】常用语法及函数的归纳总结
在练习过程中,也涉及了许多入门课程中未接触到的知识,因此在这个部分进行查缺补漏及进阶知识的学习、归纳总结。
下划线的用法介绍
单下划线——临时变量
在 Python 中,单独的下划线(
_
) 是一个特殊符号,通常用于表示临时变量或占位符。主要有以下用途:
- 忽略不需要的值
在解包赋值或循环中,用
_
忽略不需要的变量,提高代码可读性:- 交互式环境中的「上一个结果」
在 Python 交互式终端(如 IPython)中,
_
表示最后一个表达式的结果:前导单下划线和后缀单下划线
类似
_x_ticks
这样的变量,前导单下划线约定表示该变量/方法为“内部使用”,例如模块内的非公开变量、类中不暴露给外部的属性等;类似
class_
这样的变量,其下划线后缀用于避免关键字冲突。当变量名与 Python 关键字冲突时,可用下划线后缀(如 class_
代替 class
)。栈的简单使用
栈(Stack)是具有的后进先出(LIFO)特性的一种容器,可以用它来解决字符串反转的问题。
可迭代对象与迭代器
可迭代对象就是我们之前学过的列表、元组、集合等;
在介绍迭代器之前,需要先引入一个 python 中的概念——惰性求值。
惰性求值(Lazy Evaluation)是一种编程语言中的计算策略,它的核心思想是:只有在需要时才计算表达式的值,而不是立即计算。
这种方式可以节省计算资源,尤其是在处理大量数据或无限序列时非常有用。
而迭代器就是采用了惰性求值的策略,与可迭代对象一样,它可以进行遍历,但是与可迭代对象不同的是,迭代器只能够遍历一次,遍历完成后就会消失,不占用额外内存,如果需要再次使用需要再次生成;而可迭代对象即便遍历完成,各个元素值还是会保留在内存中。
因此,在处理大量数据的时候,为了节省内存,我们常会使用迭代器。
生成器对象的优缺点:
优点:
- 不占用额外内存,只在需要时生成数据;
- 适合用于大规模数据或是无限序列中;
缺点:
- 迭代完成后值会立即销毁,不保留在内存中;
- 无法像可迭代对象一样使用索引或切片;
也就是说,如果有一个迭代器
gen
,那么使用 gen[1]
或 gen[1:-1]
这样的语法都是错误的。生成器对象如果要取出下一个元素的值,只能通过
next()
方法,即 next(gen)
将值从第一个往后一个一个取出。那么,如何生成迭代器呢?
主要有以下途径:
- 利用内置函数
iter()
;
- 利用
yield
关键字定义生成器;
代码示例
- 通过 python 的内置函数产生,如
map()
函数(与pyspark
中的map
算子类似,这里仅给出示例,后面会详细介绍)。
如何使用迭代器呢?
使用方法:
- 通过
next()
函数一个个取出(可以配合 while 循环);
- 直接应用于
for
循环中取值。
在使用 迭代器 时有一个注意点:迭代器对象在做出转换后也会耗尽。如
list(gen)
将迭代器转换为列表。推导式的介绍
推导式(Comprehensions)是 Python 中快速构建数据结构的简洁语法,支持列表、字典、集合和生成器。
列表推导式
列表推导式(List Comprehension)是 Python 中一种简洁的创建列表的语法,可替代传统的
for
循环和 append
方法。- 基本形式
示例:
2. 带条件的推导式
示例:
3. 多重循环
示例:
字典推导式
快速生成字典,键值对灵活处理。
语法:
示例:
集合推导式
生成无序且元素唯一的集合,自动去重。
语法:
示例:
生成器推导式
生成器推导式与列表推导式的使用方法一致,只不过列表推导式是放在方括号
[]
中的,而生成器推导式是放在圆括号 ()
中的。若推导式超过两行或含多层嵌套,建议改用普通循环,这样代码可读性更强。
常用的内置函数和语法
any()
内置函数
any()
会遍历数据容器中的每个元素。判断数据容器中是否至少有一个元素为 True.对每个元素进行布尔值判断:
- 如果元素为
True
(或等价于True
,如非零数字、非空字符串、非空列表等),any()
立即返回True
,并停止遍历。
- 如果所有元素都为
False
(或等价于False
,如0
、None
、空字符串、空列表等),any()
返回False
。
all()
内置函数
在 Python 中,
all()
是一个内置函数,用于检查一个可迭代对象(如列表、生成器表达式等)中的所有元素是否均为 True。三元表达式 … if codition else …
表达式的语法如下:
value_if_true if condition else value_if_false
,condition
表示判断条件,value_if_true
表示条件为真(True
)时执行的表达式,value_if_false
表示条件为假(False
)时执行的表达式。enumerate()
内置函数(提取列表的索引和值)
enumerate
用于在遍历列表(或任何可迭代对象)时,同时获取元素的索引和值。它的作用是让代码更简洁、易读,避免手动维护索引变量。enumerate
返回一个 枚举对象,它是一个生成器,每次迭代生成一个元组 (index, value)
。eval()
内置函数(字符串→表达式)
eval()
是 Python 的内置函数,用于将字符串作为 Python 表达式来执行,并返回表达式的结果。如果字符串的内容是一个合法的 Python 表达式,
eval()
会返回该表达式的结果。例如:
eval("1 + 2")
返回3
。
eval('"Hello"')
返回字符串"Hello"
。
eval('("Hi", 2)')
返回一个元组("Hi", 2)
。
eval()
函数常常与 input 函数搭配使用,因为 input()
会将用户输入的内容以字符串的形式读取。sum()
内置函数(求和)
基本语法:
参数:
iterable
:一个可迭代对象(如列表、元组、集合等)或者是一个迭代器,其中的元素必须是数字类型(整数、浮点数等)。
start
(可选):累加的初始值,默认为0
。
返回值:
- 返回可迭代对象中所有元素的总和,加上
start
的值。
map()
内置函数(映射)
在黑马老师PySpark
的入门教程中,曾经介绍过pyspark
库的map()
方法(map()
算子),实际上在python
内部就内置了这个函数,使用方法与map()
算子基本一致。
map()
是 Python 中的一个内置函数,用于将一个函数应用于一个或多个可迭代对象(如列表、元组等)中的每个元素,并返回一个迭代器。map()
的核心思想是“映射”,即将一个函数映射到可迭代对象的每个元素上。通过如下的代码示例会对 map() 函数会有更好地认识:
fromkeys()
内置函数(迭代对象→字典)
python 3.7 以上版本引入的一个功能。
该函数可以将可迭代对象中的元素转换为字典中的键,并将键的值都设置为
None
。比如:
利用字典中键不会重复的特性,可以使用
fromkeys()
这个函数实现去重操作。divmode()
内置函数(获取一个数的商和余)
divmod()
是 Python 的内置函数,同时返回一个数除以另一个数的商和余数。常用功能的实现
【原课程中】嵌套列表的排序方法
【回忆】.sort()
方法和 sorted
函数
主要区别:
sorted()
是内置函数,而.sort()
是内置方法,内置函数和内置方法的调用方式不同;
内置函数:sorted(排序对象)
; 内置方法:排序对象.sort()
sorted()
不能修改原操作对象,而是会将排序后的列表返回给一个新的对象;.sort()
直接对原操作对象进行修改。
基于第二条的特性,引入第三条区别。
- 由于
.sort()
直接对原操作对象进行修改,因此.sort()
只能对列表进行操作;而sorted()
是返回给一个新列表,因此对于五种容器类型都可以进行排序,即既可以对列表进行操作,也可以对元组、字典进行操作。
值得注意的一点是,sorted 也可以对字符串和集合进行排序,它可以将字符串和集合中的每个字符提取出来进行排序,并返回给一个列表。
相同点:
都可以对列表进行排序,排序后的结果都是列表。
嵌套列表的排序方法
对于嵌套列表而言,可以使用匿名函数的方式来进行排序。
如何实现列表的去重操作
- 通过集合来实现,缺点是会打乱原列表顺序;
具体的代码为:
list(set(list_to_convert))
,复杂度为 O(n)
。- 通过遍历+记录实现,可以保留原列表顺序,缺点是要写比较多代码,复杂度为
O(n)
;
- 【python 3.7 及以上版本】通过字典的键不重复的方式来实现,复杂度为
O(n)
;
- 通过列表推导式去重,可以保留原列表顺序,缺点是复杂度为 。
如何取出列表中的唯一元素
- 利用字典来实现唯一元素的计算。
在入门教程中的“类和对象”综合案例(Ctrl+F 搜索
“老师的代码”
)那一节的字典使用技巧来实现唯一元素的计算。利用上述的代码也可以实现寻找列表中出现过两次、三次或更多的元素,灵活性高,且代码的时间复杂度为
O(n)
(这里注意,字典中找键的内部逻辑是通过哈希表来实现的,因此找键这个过程的复杂度为 O(1)
,而列表推导式为 O(n)
,因此最终的复杂度为 O(n)+O(n x 1)
)。- 利用列表推导式来进行计算。
上述方法相较于第一种方法虽然代码量小了很多,但是时间复杂度高,为 (因为列表推导式本身的复杂度为
O(n)
,而 .count()
方法又要遍历一次列表复杂度也为 O(n)
,因此最终的复杂度为 O(n x n)
),不利于处理列表数据量大的情况。统计字符串中字符个数
collections.Counter()
方法
利用
collections
模块中的 Counter
方法可以实现这个功能。该方法可以统计可迭代对象(如字符串、列表)中元素的出现次数并将结果存放在字典中。
使用示例:
假如现在有这样一个题目(题目来源):
编写一个Python程序来判断两个给定的字符串是否是错位词。如果两个字符串具有相同的字符,但顺序不同,则被认为是彼此的错位词。 例如,restful
和fluster
是错位词。
- 定义函数
are_anagrams()
,有两个参数:string1
和string2
。
- 在函数内,如果两个字符串是错位词,则返回
True
,否则返回False
。
代码:
collections.defaultdict()
方法
在 Python 的
collections
模块中,defaultdict
是一个带有默认值功能的字典。它与普通字典 dict
的最大区别在于:当访问不存在的键时,defaultdict
会自动创建该键,并用指定的默认类型初始化一个值,避免抛出 KeyError
异常。初始化方式:
defaultdict(default_factory)
。default_factory
是一个函数(如 int
, list
, lambda
),用于生成默认值。下面通过使用
defaultdict()
和不使用的两个示例来进行对比:python 中有一个内置方法与
defaultdict
类似,即 get(key, default)
方法。在 Python 的字典(
dict
)中,get(key, default)
方法用于安全地获取键对应的值,其核心作用是:当键不存在时,返回指定的默认值(default
),避免抛出 KeyError
异常。一些初始化技巧
初始化列表
res = [float('inf')] * len(nums)
通过这行代码可以创建一个长度为 nums 的列表,且列表中的每个元素都是无穷大。float('inf')
的含义:inf
是 "infinity" 的缩写,代表正无穷大,是一个特殊的浮点数值。在算法中,通常用它表示一个“极大值”,后续可以通过比较(如取最小值)被更小的实际值覆盖。
【Python 入门后】PYTHON 实用库的学习笔记
这部分内容会随着个人的学习持续更新。
命令行
【深度学习】argparse 库
argparse
库是用于做命令行解析的库,通常用在有大量参数且参数的值需要频繁修改或者需要在多个代码文件中共享参数配置的情形下,最常用的就是在深度学习领域了。通过
argparse
库,我们可以快速地在命令行中了解代码里所有参量及其用处,并且为参量赋值。在使用
argparse
库共享参数配置时,各个参数值都会排列的很整齐,同时各个参数的作用一目了然。我们可以将
argparse
库当作是一个提前制做好的模板或一套我们自己设定的标准,就像是类和对象中的工厂类一样,等学完 argparse 库,你会有更深的认识。argparse
库是 python
的内置库,要使用 argparse
库,需要在代码开头导入模块 import argparse
。接下来就按照三个步骤来进行使用即可:
1. 创建解析器
具体的语法如下:
可以将创建解析器的过程理解为是 类与对象 里面的创建对象,即创建一个解析器对象,在之后就可以使用这个对象来调用
argparse
中的各种方法了。另外说明,如果要创建多个解析器,与创建多个对象一样,使用多个解析器名就可以。
2. 添加参数和参数配置
使用
argparse
的 .add_argument
方法可以设置解析器内部的参数,参数包含可选的和必选的。前面我们介绍过,argparse 可以在命令行中快速的修改参数值,但在参数数量很多的情况下,我们不可能将所有参数都一个一个赋值。
因此根据目标的不同,我们会在每次实验中更改不同的参数(控制变量法),而那些在本次实验中不研究的参数,可以让它赋默认值,等需要的时候再进行更改。
要实现这样灵活的功能,我们就需要合理的安排(通过
.add_argument
合理设置)可选参数和必选参数。下面我会先介绍如何在代码中设置参数,然后再介绍如何在命令行和代码中调用并修改这些参数。
最基础的 argparse
设置参数的方法
在这个部分,大家将学会如何创建位置参数(必选参数)和可选参数。
下面这条语句创建了一个位置参数,这是创建参数最简单的方式,只需要在括号中放入一个字符串,就可以创建一个位置参数。
.add_argument()
括号中放入的字符串即为参数的名字,在上面的语句中我创建了一个名为 “位置参数” 的参数,你可以将它理解为一个变量,变量中默认存储的数据类型是字符串。argparse 中的参数名字最好都设置为英文,但在本篇教程中,为了方便大家理解,就使用中文的名称来介绍了。
下面创建可选参数,通过在参数名前面加上两个减号
--
来创建可选参数。上面这条语句很轻松就创建了一个名为 “可选参数” 的参数名,默认存储的数据类型也是字符串。
在实际应用中,使用两个减号创建的可选参数的名字一般是全名,因此它通常会比较长,为了在命令行中更方便的调用和赋值,我们会给可选参数起一个别名。别名通过一个减号
-
来进行创建。需要注意的是,必选参数无法设置别名,只有可选参数可以。在上面这行语句中,创建了一个全名为
your_full_name
的参数,参数的别名为 n
,在后面调用参数及赋值的时候,我既可以使用参数的全名,也可以使用参数的别名。argparse
帮助文档及命令行调用
在这个部分,大家会学会如何在命令行中查看先前设置参数的帮助文档以及如何在命令行中调用参数。
通过上面基础语法的介绍,我们已经能够在
argparse
中设置参数了,不过此时我们还难以有实在的感受,参数虽然设置了,但我们该如何查看、修改它呢?想要查看、修改这些参数,可以在命令行中进行,也可以在代码中进行(关于如何在代码中修改参数会在后面的“解析参数”部分介绍)。
如何打开命令行提示符?
第一种方法:
直接通过
pycharm
进行操作。在 pycharm 的交互显示窗口中找到下图所示的终端然后点击进入。检查
PS
后面的项目路径是否为自己将要运行的代码文件的项目路径,若不是,需要 cd 文件夹名称
(可以为中文)先进入文件夹,然后再进行后面的操作。
第二种方法:
直接在要进入的文件夹中输入 cmd,这样 cmd 中的默认地址就已经是项目地址了。
第三种方法:
首先按下
Win+R
,在弹出的窗口中输入 cmd
即可打开命令行提示符。如果使用的是第三种方法,那么在命令行中,我们需要先输入盘符 (如
D:
)进入对应的磁盘,然后再通过 cd 项目路径
来进入我们的项目文件夹。比如我的项目在 D:\test
中,我就需要在命令行中先写上 D:
进入 D 盘,然后通过cd D:\test
进入项目文件夹(注意 cd
后面有空格)。帮助文档的查看及 --help
参数的介绍
之后要进行的步骤以下面所示的代码为基础:
在打开命令行提示符并进入相应的项目文件夹后,输入
python 测试文件.py -h
即可查看 argparse
的帮助文档,帮助文档中包含在代码中定义的所有必选参数和可选参数,同时帮助文档的开头为 description
中的内容 “argparse 示例教程”:在上面的帮助文档中,我们注意到有一个可选参数是我们没有定义的,即
--help
参数,它是解析器创建每个参数时都会自带的一个可选参数,别名是 -h
,有了它,我们可以很方便的向别人解释这个参数的作用。命令行中修改参数
我们先介绍参数在命令行中的修改方式。后面的介绍以上面的代码为基础:
在前面,我们通过
python 测试文件.py -h
语句在命令行中查看了帮助文档,这条语句中的 python 测试文件.py
前缀表示对 测试文件.py
进行 python 相关操作。具体操作的内容是什么,需要看 .py
后面的指令内容,如 -h
。因此如果我们需要修改参数,我们就需要先指定对哪个文件操作,先写上固定前缀
python 测试文件.py
。在修改参数之前,我们必须注意,位置参数(必选参数)是必须要赋值的,且必须按顺序赋值。如果想要不按顺序赋值(类似于函数中的关键字传参),就需要使用可选参数(后面会介绍一个
required
方法可与之很好的配合)。位置参数(必选参数)的赋值(修改):
比如有一个人是张三,北京市的,我们可以这样写
python 测试文件.py 张三 北京
,输出的结果为 Namespace(姓名='张三', 省市='北京', 性别=None, age=None)
。(Namespace 表示存储解析后参数的对象的空间,它是一个类似于字典的对象,可以通过属性名访问参数值。)在 Namespace 中,我们可以看到,
姓名
就已经赋值为了 "张三"
,省市
被赋值为"北京"
,而性别
和 age
是可选参数,在没有赋值的情况下为 None
。可选参数的赋值(修改):
比如北京市张三的年龄为 18 岁,性别为男,我们就可以在命令行中这样赋值,
python 测试文件.py 张三 北京 --性别 男 --age 18
输出的结果为:Namespace(姓名='张三', 省市='北京', 性别='男', age='18')
(注意这里的年龄为字符串类型,后面会教大家如何修改类型);可选参数的赋值可以不用像位置参数一样按顺序赋值,因此后面这条语句的效果与前面是一致的: python 测试文件.py 张三 北京 --age 18 --性别 男
。在前面的代码中,我们给
age
这个可选参数设置了一个别名 年龄
,在命令行中赋值的时候,可以使用年龄
来进行赋值,如 python 测试文件.py 张三 北京 -年龄 18 --性别 男
。在使用别名进行赋值时,别名前面只能有一个减号,否则会报错。argparse
的常见参数及用法
argparse
参数设置时有一点需要注意:argparse
的位置参数要放在关键字参数前面,关键字参数内部可以随意顺序。
type
→ 设置参数类型
在前面我们介绍过,解析器设置的参数默认都是字符串类型的,如果想要将参数设置为其他类型,比如把年龄设置为数字类型,可以使用
type
参数。type 参数可以设置的类型有
int
,float
,str
,bool
(bool
需结合 action
参数使用)。type 参数的使用方法如下:
default
→ 设置参数默认值
default
是用于设置参数默认值的,这样在参数没有在命令行中赋值的情况下,它也能有一个值,而非 None
;可选参数和位置参数都可以使用
default
参数,先介绍可选参数 default
的使用,再介绍位置参数 default
的使用。可选参数在设置了
default
的后,在我们没有显式为其复制的情况下,可以使用默认值。使用的示例如下:对于位置参数而言,情况会稍复杂一些,如果设置了
default
参数,在命令行中仍然需要显式的为参数赋值;那么既然都需要赋值,
default
参数在位置参数中是否并无用处呢?并非如此,在位置参数中,
default
需要与后面将会介绍的 nargs
参数搭配使用,nargs
参数用于控制参数接受值的数量。下面先举一个例子,在下面的例子中会使用到
nargs
参数,我们只需要先知道 nargs="?"
表示接收 0 或 1 个参数:nargs
→ 参数接收数量设置
在使用nargs
设置位置参数的接收数量时需要记住:参数接收值越多的参数应放在越后面定义。如果使用可选参数,那么没有此限制。
首先说明一下 nargs 的取值,nargs 可以有以下这些取值:
- 直接指定参数数量(整数);
?
:表示可以接收 0 或 1 个值;
+
:表示必须接收至少 1 个值;
*
:表示接收任意数量参数(包括 0 个);
下面是该参数的代码示例:
choices
→ 限制可选项
即参数的值只能从给定的列表值中选择,否则会报错。
required
→ 是否必须(仅用于可选参数)
required
为 True
时,意味着可选参数是必须要赋值的。利用
required
参数,我们可以将可选参数当作是必选参数来用,同时用能够不按照顺序赋值。metavar
→ 帮助文档中的参数占位符
metavar
常用做帮助文档中参数的精简说明,而 help
常用于详细地说明。dest
→ 解析结果的属性名
这个参数可以在学习完“解析参数”部分后再查看。
这个参数类似于可选参数中的别名(之前介绍过,使用一个短斜杠 “
-
” 定义),但与别名不同的是,dest
参数设置的名字是在代码中调用并赋值的,而通过 “-
” 方式定义的别名只能够在命令行中使用。dest
参数本质上是将原来的参数名进行替换,替换后原参数名就不会发挥作用了。但是,
dest
替换后的参数名无法在命令行中使用,命令行中只能使用全名和短斜杠定义的别名。这里假设大家已经阅读完解析参数部分的内容了。下面是关于该参数的代码示例:
使用 dest 参数时需要注意以下几点:
- 位置参数无法使用 dest 属性;
- 使用了 dest 参数更换参数名后,原来的全名就无法使用了;
action
→ 参数处理动作(一般不用)
action
参数决定了如何处理命令行输入的参数值,是参数解析的核心控制开关。通过不同动作类型,可以实现存储值、统计次数、触发操作等功能。action
参数在我们不指定时,默认的值是 store
,即存储用户输入的参数值(显式赋值),如 python 测试文件.py --name "张三"
,给 name
参数赋值张三
。如果我们要指定 action 参数,通常有三个参数可以选择:
store_true
/store_false
;
用于让参数存储布尔值,如果
action
为 store_true
,则其布尔值默认为 False
;如果 action
为 store_false
,则其布尔值默认为 True
。在命令行中,当我们显示调用该参数(不需要为其赋值)时,参数的布尔值就会反转。
如,我们通过代码
parser.add_argument("--bool", action="store_true", help="存储布尔值,默认为 False")
定义了一个可选参数 bool
,它内部的值默认是 False
,但在命令行中我们输入 python 测试文件.py --bool
时,bool
参数的值就会反转为 True
。append
;
用于收集多个参数值到列表,相当于是将参数类型设置为了 list 型,可以存放多个值;
在命令行中,我们可以通过多次调用参数并赋值来实现添加列表元素。
如,我们通过代码
parser.add_argument("--list", action="append", help="将对参数多次赋值的结果以列表形式存放")
定义了一个可选参数 list
,在命令行中我们输入 python 测试文件.py --list "张三" --list "李四"
就可以向 list 参数中添加 "张三"
和 "李四"
两个元素。count
;
用于统计参数出现次数(参数的值就是参数调用的次数),我们在命令行中每调用一次参数,参数内部的值就会加 1;
如,我们通过代码
parser.add_argument("--count_num", action="count", help="记录参数调用次数")
定义了一个可选参数 count_num
,在命令行中我们输入 python 测试文件.py --count_num --count_num
,由于调用了两次 count_num
参数,因此参数内部存储的值就是 2。3. 解析参数
以下的介绍基于下面的代码:
如果要在代码中为参数赋值,参数名最好不要使用中文,如果一定需要使用中文,那么请将中文参数设置为可选参数,这样可以通过
dest
参数来设置英文属性名,如 parser.add_argument("--姓名", "-name", dest="name", help="用于记录用户的姓名")
。在离开本篇教程后,大家自己使用 argparse
库时,尽量都使用英文来命名参数。解析命令行参数首先要创建一个解析对象,创建解析对象需要调用解析器的
.parse_args()
方法。具体代码如下:不要将创建解析器对象的代码和创建解析器的代码混淆。
创建解析器:
parser = argparse.ArgumentParser(description="解析器")
创建解析器对象:
args = parser.parse_args()
它们的名字通常使用
parser
和 args
,当然也可以自定义。创建了解析器对象以后,如果需要将其打印输出需要进行如下操作:
如果我们创建了两个解析器对象怎么办呢?命令行中还能进行赋值操作吗?
先看下面的代码:
在命令行中,如果我们想要更改解析器对象 args_1 的 name,我们会使用命令行语句
python 测试文件.py —name 张三
。此时按下回车,我们会发现两个解析器对象都被修改了,因此在命令行中很难将两个解析器对象分隔开来赋值。
当然,一个可行的解决方案是,干脆创建两个解析器,每个解析器再单独创建一个解析对象:
上面这种方式是可行的,但给人的感觉总有点不优雅,或者说有种代码 “冗余” 的错觉。
为了要实现两个解析器对象的分别赋值,我们可以采用在代码中修改参数的方法。
如何在代码中修改参数?
有些人可能会疑惑,我们一开始使用 argparse 库的目的之一不就是为了能够在命令行中快捷修改参数,或是临时改变参数值吗?这样不会与我们的目标背道而驰吗?
确实是,如果我们使用了两个解析器对象,在命令行中就很难对两个对象进行调控。
即便只是创建了一个解析器对象,但在代码中对参数进行赋值的方式,也会使得该参数在命令行中的值无法被修改。
因此,在代码中为参数赋值,多半是出于共享多个代码文件中参数配置的目的。
在代码中修改参数的时候不要使用短斜杠形式命名的别名,比如定义了一个参数
parser.add_argument("--gender", "-gen", help="记录性别")
,在代码中就不能使用 gen
这个别名来对参数赋值或打印输出,否则会报错。首先,我们看看如何在代码中为参数赋值?
通过
args.参数名
访问参数值,并直接在代码中使用。比如:接下来,我们看看如何创建两个解析器对象并赋值。
argparse
使用案例
这个部分是部分深度学习的代码摘录,让大家对argparse
库的使用有一个直观的理解。
数据分析
数据分析的基本流程
数据分析就是将离散的数据转换为可视化的图表,以更清晰的观察数据的趋势变化。
数据分析的基本流程如下:

【学习中…】matplotlib 库
主要用于绘制可视化图形,名字取材于 matlab。
折线图的绘制
绘制图形的核心
使用 matplotlib 库中的函数来绘图,核心是确定好 x 轴使用哪些点,y 轴使用哪些点,也就是提前准备好数据。
而具体这些点如何绘制成线是 matplotlib 内部帮助我们完成的,我们的任务就是把点找到传入对应的函数中。
pyplot
模块的介绍
matplotlib.pyplot
是 matplotlib
库的核心模块,提供了一套类似于 MATLAB 的绘图接口,支持快速生成各种类型的图表(折线图、柱状图、散点图等)。在后面的笔记中,我都会使用
from matplotlib import pyplot as plt
来导入pyplot
,同时使用别名 plt
来调用它。【回忆】range()
函数
用于创建一个等差数列。

简单案例:

案例求解:
绘制简单的折线图(介绍两个基本方法—— plt.plot()
和 plt.show()
)
要绘制折线图,首先要导入 matplotlib 库,使用 matplotlib 库中的 pyplot 方法就可以就可以绘制图形了。
【回忆】如何安装 python 的库?
- 通过命令行提示符下载。
按下Win+R
输入 cmd 回车打开命令行提示符,在里面输入pip install [包名]
即可安装相应的包,如pip install numpy
。如果你拥有“科学上网”的能力,那么你的下载速度应该比较快;如果你没有,那么就需要利用清华镜像站去下载,使用pip install -i https://pypi.tuna.tsinghua.edu.cn/simple [包名称]
即可。
如果你安装了 anaconda,那么请通过这个方式安装
首先,按下
Win+R
输入 cmd 打开命令行窗口,通过 conda activate [你的环境名称]
(假设你先前已经创建过环境了)的方式进入虚拟环境,然后输入 conda install [包的名称]
的方式即可下载包了,比如 conda install numpy
。如果你之前没有创建过环境,那么请你先通过指令
conda create -n [环境名称] python=3.x
的方式创建环境,比如 conda create -n py_learn python=3.x
,然后通过 conda activate py_learn
即可打开。- 在 pycharm 中进行下载,我是可以正常进行下载的,不过有些人说需要能够上外网才行,可以自己试试。

plt.plot(横轴, 纵轴)
方法:
用于绘制图像并暂时保存(不会保存在本地)。其中横轴和纵轴都放置列表,列表的长度需要相同,因为 x 和 y 要一一对应。可以使用变量存放横纵轴的值。
横纵轴的值也可以是字符串,只要长度相同,能够一一对应即可。
plt.show()
方法:
用于将
.plot()
保存的图像显示出来。一般 .show()
方法和 .plot()
方法是搭配使用的。完整代码:

【案例】第一个案例
案例:
假设一天中某 12 小时(
range(12)
)的气温(℃)分别是 [15,13,14.5,17,20,25,26,26,27,22,18,15]
。代码:
绘制结果:

创建画布(fig = plt.figure(figsize=, dpi=)
)
在我们没有使用指令创建画布时,python 内部会隐式地帮我们自动创建一个画布,在这个画布中,可以显示我们绘制的图像。
但是,如果我们想要实现图像的大小更改,图像的分辨率(控制图像清晰程度)的设置,创建子图像或多个独立画布等操作时,就需要使用指令创建画布。简而言之,画布可以帮助我们实现更多的功能。
通过
plt.figure(figsize=(宽, 高), dpi=...)
即可实现画布的绘制,其中宽和高的单位都是英寸, dpi 表示 dots per inch,即每英寸绘制多少个点,每两个点之间是一个像素。也就是说,如果我通过
plt.figure(figsize=(10, 20), dpi=10)
绘制了图像,那么图像最终的大小就是 100 * 200
,图像宽为 10 英寸,100 个像素,高为 20 英寸,200 个像素。也可以将 figure 的结果赋给一个新变量,比如
fig = plt.figure()
。可以把 fig 看作是一个对象,后续可以调用 fig 来实现更多调整(后面会介绍)。x 轴和 y 轴刻度值的设置(plt.xticks()
,plt.yticks()
)
简单设置(自定义刻度)
在前面绘制的图像中,横轴和纵轴刻度是 python 内部自动生成的,如果我们想要自定义刻度大小,就需要利用本节的内容。

设置横轴的刻度:
plt.xticks(ticks)
,其中“ticks
”可以用列表,也可以使用元组或 numpy 数组(在后面会介绍)。这条语句是 Matplotlib 中用于 自定义 x 轴刻度位置和标签 的。比如原先传入的 x 坐标为
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
,但通过 plt.xticks([2, 4, 6])
语句就可以在横轴上只显示 x[2]
、x[4]
和 x[6]
处的 x 坐标,即横轴上只显示 3,5,7 这三个数字(列表第一个元素为 x[0]
)。设置纵轴的刻度:
plt.yticks(ticks)
,除了方法名字不一样外,其他都与 plt.xticks()
相同。
当我们具有大量的数据点的时候,
plt.xticks
可以使用切片来表示:

高级设置(自定义刻度文本、刻度值旋转)
下面的介绍主要以plt.xticks()
为主,但plt.yticks()
也是通用的。
自定义刻度
原先的语句(
plt.xticks()
)可以自定义 x 轴显示的刻度,如果我们想将显示的刻度替换为其他文本或数字,如何操作呢?可以通过
plt.xticks(ticks, labels)
,其中,ticks
和 labels
都可以使用列表来表示,但是它们的长度需要一样,这样文本的第一个元素就会替换刻度的第一个元素;文本的第二个元素就会替换刻度的第二个元素,以此类推。输出结果:

刻度值的旋转
plt.xticks(ticks, rotation=...)
rotation 如果填写正数,表示顺时针旋转,如果填写负数,表示逆时针旋转。输出结果:

【补充】变量的前导下划线和单尾下划线介绍
类似
_x_ticks
这样的变量,前导单下划线约定表示该变量/方法为“内部使用”,例如模块内的非公开变量、类中不暴露给外部的属性等;类似
class_
这样的变量,其下划线后缀用于避免关键字冲突。当变量名与 Python 关键字冲突时,可用下划线后缀(如 class_
代替 class
)。保存折线图(plt.savefig("路径")
)
通过
plt.savefig("保存路径")
一条语句即可进行保存。【回忆】两种保存路径的介绍——绝对路径和相对路径
路径(Path) 是文件的“地址”,告诉计算机去哪里找到文件或文件夹。
路径有两种表示方法:绝对路径和相对路径。
绝对路径指的是以根目录(带盘符)开始的路径,使用
盘符:/
(盘符如 D:/
或 C:/
或F:/
等)或 盘符:\\
(这里之所以是两个正斜杠是因为在 python 中 \
是转义字符)表示。如 D:/test
就表示根目录下的 test
文件夹(D:\\test
也可以表示根目录下的 test
文件夹)。相对路径指的是文件或文件夹相对于当前代码文件存放的文件夹的路径,比如代码文件存放在
D:/test
文件夹下,那么在这个文件夹中的 hello.txt
文件就可以表示为 hello.txt
,这个路径下的文件夹 folder
中的文件 hello2.txt
就可以表示为 D:/folder/hello2.txt
。另外还需要知道两个符号
./
和 ../
。./
表示的是当前文件夹,D:/test
文件夹下的 hello.txt
文件就是 ./hello.txt
,这个路径也是相对路径,通常用于要在当前文件夹下创建新文件时使用。../
表示的是当前文件夹的上一级文件夹,比如 D:/test
文件夹的上一级文件夹就是盘符 D:/
,因此假如我们使用路径 ../test2
就表示 D:/test2
的意思。在 python 中,路径需要放在引号中作为字符串,因此,除了
盘符:/
和 盘符:\\
两种方式表示路径外,也可以使用 r"盘符:/..."
来表示路径,前缀 r 表示忽略引号中所有的转义字符。比如 r"D:\test\hello.txt"
。使用时需要注意,这条语句必须在
plt.plot(x, y)
后面调用。

设置横纵坐标轴的标签及图像的标题(plt.xlabel()
、plt.ylabel()
、plt.tilte()
)
通过
plt.xlabel("name_x")
、plt.ylabel("name_y")
两条语句即可设置坐标轴的标签,通过 plt.tilte()
即可设置图像的标题。输出结果

在上面的图像中,横纵坐标和标题都是使用英文表示的,但很多时候,我们希望将图标呈现给中国人看,因此我们需要将标签和标题改为用中文显示,后面的部分就是中文字体的介绍。
中文字体的设置(matplotlib.rc()
)
matplotlib 默认是不支持中文的,需要手动更改字体才行。
matplotlib
有两种方式可以更改字体:matplotlib.rc()
这种方法的好处是可以全局设置 matplotlib 的字体,不过似乎只能用于 Windows 和 Linux 系统中。使用方法如下:
font
常用的字体(亲测有效)
经过测试,下面的几个字体名称都可以使用。
中文名 | 字体名 (英文) |
黑体 | SimHei |
微软雅黑 | Microsoft YaHei |
宋体 | SimSun |
楷体 | KaiTi |
仿宋 | FangSong |
华文细黑 | STXihei |
华文宋体 | STSong |
输出结果:

font_manager()
这种方法会稍微麻烦点,但是 Windows 、 Linux 和 Mac 系统都是通用的。
如何查找字体文件?
首先打开设置,在搜索框中输入 “字体设置”并选择,找到你想要用的字体后点进去。

点进去后,找到字体文件的路径,将其输入到代码中。(由于无法复制,可以使用 QQ 或者是什么截图软件 OCR 识别后粘贴,但要确保识别后的内容不会多出一些空格或其他字符。或者找一个路径短一点的,比如微软雅黑)

【案例】第二个案例
案例:
如果列表 a 表示 10 点到 12 点的每一分钟的气温,如何绘制折线图观察每分钟气温的变化情况?
a= [random.randint(20,35) for i in range(120)]
代码:
输出结果:

绘制网格(透明度)
输出结果

输出结果

多折线绘制(图例、曲线颜色、线型、线条)
当我们需要在一副折线图中观察多幅图像的时候,需要利用本部分的知识。
绘制多折线
输出结果

【优化】添加图例
输出结果

调整图例的位置(在
legend
中加入 loc
参数):
在查看帮助文档时,按照老师的方法,使用
Ctrl+鼠标左键
点按函数没有找到上图的内容。上图是通过终端中导入 matplotlib.pyplot
后输入 help(plt.legend)
打开的帮助文档中找到的(如下图所示)。help()
函数查找方法

loc
参数既可以使用上图中的字符串,也可以使用上图中的数字。输出结果

中文显示:
在这里需要特别注意以下
plt.legend()
中文显示的问题,如果使用 matplotlib.rc()
方法设置全局中文,plt.legend()
不需要额外设置。如果使用
font_manager()
来配置中文,那么在其他函数中需要加上 fontproperties=my_font
这样的参数,而在 plt.legend()
中比较特殊,加上的是 prop=my_font
。老师说,其他的像 xticks()
这些函数全部都是 fontproperties
的参数,只有 legend()
特殊,需要特别注意。【优化】曲线颜色、线型、线条
在plot
函数中增加参数。

散点图的绘制
散点图的绘制可以说除了绘图的函数不同外,其他和折线图的绘制是一样的。
折线图使用函数
plt.plot()
来绘制图像,而散点图使用 plt.scatter()
函数来绘制。由于大体内容都相似,因此本部分直接通过案例来介绍,不会再一一介绍语法。案例:
假设通过爬虫你获取到了北京 2016 年 3,10 月份每天白天的最高气温(分别位于列表 a,b),那么此时如何寻找出气温和随时间(天)变化的某种规律?
y_3 = [11, 17, 16, 11, 12, 11, 12, 6, 6, 7, 8, 9, 12, 15, 14, 17, 18, 21, 16, 17, 20, 14, 15, 15, 15, 19, 21, 22, 22, 22, 23]
y_10 = [26, 26, 28, 19, 21, 17, 16, 19, 18, 20, 20, 19, 22, 23, 17, 20, 21, 20, 22, 15, 11, 15, 5, 13, 17, 10, 11, 13, 12, 13, 6]
最终绘制出的效果图

代码:
条形图(柱状图)的绘制
条形图的回执与折线图、散点图也是类似的,绘图的函数为
plt.bar()
,这个函数绘制的是竖条形图;如果要绘制横向的条形图(横轴标签比较长的时候),需要使用
plt.barh()
函数进行绘制。bar()
函数的基本语法
x
:柱子的 x 轴位置(即自变量,通常是类别标签对应的数值或位置列表,可以为数字,也可以是字符串)。
height
:柱子的高度(即因变量,对应 y 轴数值的列表)。
width
:柱子的宽度(默认0.8
)。
align
: x 轴标签与柱子间的对齐方式,可选'center'
(默认,刻度位于柱子正中间)或'edge'
(刻度位于柱子左边缘)。
color
: 柱子填充颜色(支持颜色名、十六进制代码或 RGB 元组)。
常用颜色
颜色名 | 十六进制代码 | 示例效果 | 备注 |
'red' | #FF0000 | 🔴 纯红色 | 基础色 |
'blue' | #0000FF | 🔵 纯蓝色 | ㅤ |
'green' | #008000 | 🟢 深绿色 | 注意和 'lime' (亮绿)区别 |
'yellow' | #FFFF00 | 🟡 纯黄色 | ㅤ |
'black' | #000000 | ⚫ 纯黑色 | ㅤ |
'white' | #FFFFFF | ⚪ 纯白色 | ㅤ |
'orange' | #FFA500 | 🟠 橙色 | 醒目,适合高亮数据点 |
'purple' | #800080 | 🟣 紫色 | ㅤ |
'cyan' | #00FFFF | 青蓝色 | 类似浅蓝 |
'magenta' | #FF00FF | 品红色 | ㅤ |
'pink' | #FFC0CB | 粉红色 | 柔和风格 |
'gold' | #FFD700 | 金色 | 高对比度 |
edgecolor
: 每个柱子的边框颜色。
label
: 图例标签(需配合plt.legend()
使用)。
barh()
函数
barh()
函数的用法与 bar()
基本一致,有一些细微的区别如下:bar()
函数的基础定义 plt.bar(x, height, width=0.8)
,barh()
函数的基础定义 plt.barh(y, width, height=0.8)
从基础定义中可以看到,
bar()
函数使用 x
接收横坐标,height
接收纵坐标,width
表示柱子宽度;而
barh()
函数通过 y
接收纵坐标,width
接收横坐标,用 height
表示柱子宽度。这样定义的好处是
plt.bar(x, y)
与 plt.barh(x, y)
两个函数在相互转变的时候无需手动更换 x y 坐标(可以通过下面的示例来体会)。下面通过实际案例来理解
bar()
函数和 barh()
函数的使用:案例:
假设现在有 2017 年内地电影票房前 20 的电影(列表a)和电影票房数据(列表b),如何更加直观的展示该数据?
x = ['战狼2', '速度与激情8', '功夫瑜伽', '西游伏妖篇', '变形金刚5:最后的骑士', '摔跤吧!爸爸', '加勒比海盗5:死无对证', '金刚:骷髅岛', '极限特工:终极回归', '生化危机6:终章', '乘风破浪', '神偷奶爸3', '智取威虎山', '大闹天竺', '金刚狼3:殊死一战', '蜘蛛侠:英雄归来', '悟空传', '银河护卫队2', '情圣', '新木乃伊']
y = [56.01, 26.94, 17.53, 16.49, 15.45, 12.96, 11.8, 11.61, 11.28, 11.12, 10.49, 10.3, 8.75, 7.55, 7.32, 6.99, 6.88, 6.86, 6.58, 6.23]
单位:亿代码:
竖柱状图
输出结果:

横柱状图
输出结果:


从上图中,我们可以看到柱状图是已经排好序的(说明给定的 x 和 y 的数据就是排序好的数据),但最大的在下面,最小的在上面,看着很不舒服。
可以通过
plt.barh(x[::-1], y[::-1], color='orange')
将柱状图逆过来,得到:
有些时候我们得到的数据是未排序的,我们可以通过下面改进后的代码来实现柱状图的排序。
【待学习】numpy 库
【待学习】pandas 库
如果本篇笔记对你有用,能否『请我吃根棒棒糖🍭 』🥺…