02 流程控制:如何控制代码执行的顺序?

上一讲中,我们讲的所有代码都是顺序执行的。什么意思呢?就是我们在所有例子中的代码,计算机都是从第一句开始执行,执行完毕后执行第二句,以此类推,最终执行完整个代码块。

以下面的代码块为例:

print("First Line!")
print("Second Line!")
print("Third Line!")

输出结果:

First Line!
Second Line!
Third Line!

从结果可以看到,输出的内容顺序和我们写的代码顺序是一样的。像这种顺序执行的代码结构,称为顺序结构,是最普通的一种执行结构。在 Python 中,我们编写的代码默认以顺序结构执行。

顺序结构理解起来比较简单,但却不能满足所有场景的要求。

比如公司目前举办了一次全员数据安全考试,所以需要你根据员工的考试成绩进行分析。

目前有以下两个任务。

  1. 拿到一个员工的考试成绩,如果大于 80 分,则打印“优秀”;如果是 60 到 80 之间,则打印“及格”;小于 60 分,则打印“不及格”。

  2. 针对公司所有的员工的成绩,都需按 1 中所提到的方式打印出分级的结果。

上述的两个场景中,一个是需要选择性地执行部分代码,再一个是需要把同一段代码重复执行多次。这两个诉求,顺序结构无法满足。所以在 Python 中,会有分支结构循环结构来处理类似上面的场景,这也是本讲要介绍的内容——代码的流程控制。

分支结构和循环结构,整个原理比较考验逻辑性,对于初次接触编程的同学可能会有点不适应,不过不用担心,认认真真想几遍往往都能豁然开朗。从另一方面看,学 Python 还能提升自己的逻辑性和逻辑思维能力,不要太美。

分支结构

分支结构,简单来说就是如果满足某个条件,则执行某块代码,否则就不执行。

if 语句

在 Python 中用 if 语句来实现分支结构,形式是这样的。

if 条件:代码块

我们来逐一剖析下上述代码块的关键要素。

  • if,是 Python 语言的关键字,用来告诉系统,接下来是一个 if 语句。

  • 条件,与 if 之间用空格隔开,一般就是一个布尔变量(比如 a > 3 ),或者结果为布尔类型的表达式( True or False)。

  • 冒号,在 Python 语言中,冒号非常常见,一般代表冒号前面的语句对冒号后面的有控制的关系。比如对于 if 语句来说,冒号后面的代码块是否会执行,这都取决于冒号前面的条件是否满足。

  • 代码块,代码块是一行或者多于一行的简称。if 语句中的代码块在冒号后面,需要新增一行。当 if 语句的条件满足时(条件的结果为 True),则该代码块会被执行,否则就不会被执行。

  • 代码块的缩进,这也是 Python 的语法规则。当代码块隶属于某个前面的结构时,需要在前方加上四个空格,这种在代码前面加空格的行为,一般叫作缩进。主流的缩进有四个空格或者两个空格,Python 一般使用 4 个空格作为缩进。

回顾一下我们前言抛出的问题,拿到一个学生的考试成绩:

  • 如果大于 80 分,则打印“优秀”;

  • 如果是 60 到 80 之间,则打印“及格”;

  • 小于 60 分,则打印“不及格”。

我们现在用 if 语句,来实现第一部分:如果大于 80 分,则打印“优秀”。

打开 VSCode, 创建一个新的 Notebook,保存为 chapter02.ipynb,并输入以下代码。

a = 90
if a > 80 : print("优秀")
print("code finished")

上述代码中,主要由三部分组成:

  • 创建一个变量 a, 代表学生的成绩,这里我们假设这个同学的成绩是 90 分;

  • 一个 if 语句,实现了上述的任务,如果成绩大于 80 ,则打印“优秀”;

  • 打印 code finished, 打印这行没有实际意义,仅是作为代码执行完毕的标志。

上述代码输出结果如下:

优秀
code finished

为了测试在不满足条件时是否还会输出“优秀”,我们可以将 90 改为 75,再次运行程序。可以看到,这次的输出为 :

code finished

这个结果说明,我们的 if 确实起到了分支结构的作用。

if-else 语句

if 语句有几种形式,刚才我们学习的是最基础的形式,只有一个代码块,表示的是“如果满足条件,那么就做这件事”。

今天我再介绍一个升级的形式,表示的是:“如果满足条件,那么就做这件事,否则就做那件事”。可以看出新的形式里有“两件事”,所以会有两个代码块。这种形式叫作 if-else 语句(else 就代表上面描述里面的 “否则”),其实回想我们的生活中,绝大多数选择都是 if-else 结构的。

比如你出门的时候,女朋友可能会和你说:“买一个西瓜回来,如果没有的话就买一个哈密瓜”。用 if-else 的接过来理解的话就是:“如果有西瓜,那么就买西瓜,否则就买哈密瓜”。

if-else 语句形式,是这样的。

if 条件:代码块1
else:代码块2

相比 if 语句, if-else 语句的关键要素增加了以下部分:

  • else,else 和 if 一样,也是 Python 的关键字,和 if 处于同一缩进层级;

  • 冒号,由于 else 需要控制代码块 2,所以也需要在 else 的末尾增加一个冒号;

  • 代码块 2,代表由 else 控制的代码块,当条件不满足的时候执行。

上述的例子中,我们实现了如果分数大于 80 则打印优秀,现在我们加一下需求:如果分数大于 80 则打印优秀,否则打印及格。

在刚才的 Notebook, 新建代码 Cell,输入以下代码:

a = 75
if a > 80 : print("优秀")
else:print("及格")
print("code finished")

执行之后输出如下。

及格
code finished

可以看到,当条件不满足时(75 < 80), if 控制的代码块没有执行,而 else 控制的代码块被执行了。

if-elif-else 语句

接下来,我们再学习 if 语句的最后一种形式,就是在 if-else 中间再加了一个 elif(其实就是 else if 的简写),它的语句形式是这样的。

if 条件1:代码块1
elif 条件2:代码块2
...
else:代码块N

从这个结构基本可以猜出来这个语句是做什么事情:如果满足条件 1,则执行代码块 1,否则看是否满足条件 2,满足则执行代码块 2,不满足则看是否满足条件 3,……如果所有条件都不满足,则执行最后由 else 控制的代码块。
if-elif-else 语句要关注以下几个关键点:

  • elif 块可以任意多个;

  • 整个执行过程会逐一查看条件满足的情况,执行第一个满足条件的对应代码块;

  • 在执行的时候,会一个一个、从上到下的检查条件是否满足,当遇到满足的条件时,则执行该条件对应的代码块

  • else 块不是必须出现的。

现在我们再次看一下,我们提出分支结构时的背景问题:拿到考试成绩,如果大于 80 分,则打印“优秀”;如果是 60 到 80 之间,则打印“及格”;小于 60 分,则打印“不及格”。

实操开始,新建 Cell,输入代表以上意思的代码,你先根据自己理解写,写完再看示例。哪怕暂时不知道怎么写,也要先想一下,这一步至关重要,先想一下能够大幅提升学习的效率。

a = 75
if a > 80:print("优秀")
elif a > 60:print("及格")
else:print("不及格")
print("code finished")

上述代码执行的逻辑是:

  • 判断是否大于 80 ,如果大于则打印优秀,并跳过剩下的 elif、else 语句块,直接执行最后的:打印 code finished;

  • 如果不大于 80,则判断是否大于 60,大于则打印及格,并跳过剩下的 else 块,直接执行最后的:打印 code finished;

  • 如果还是不大于 60,则执行 else 块,打印不及格,并打印最后的 code finished。

这个例子还反映了一个 if 家族语句的重要特性:在一个 if-else 或者 if-elif-else 语句中,不管最后有多少个代码块,每次运行最终只会执行其中一个。 执行的是哪个呢?就是第一个符合条件的,拿上面的例子来说,a = 75,首先“是否大于 80”,明显是不满足的,所以继续看第二个条件:“是否大于 60”, 这里满足了。所以“是否大于 60”这个条件就是第一个符合条件的分支,所以其对应的代码块 print("及格")被执行。执行完毕后,便退出了整个 if 语句,下一步便执行了最后的打印 code finished 的语句。

为了验证我们的分支逻辑是否有效,我们给变量 a 分别赋值不同的分数值,比如 93、40、75 等,代码运行后,可以看到最终结果输出了对应的标签,比如以 75 分为例,结果输出为及格。

及格
code finished

至此,我们已经学完了 Python 中实现分支结构的关键要素(if 家族语句)。

  • if 语句: 判断一个条件是不是满足,满足的话就执行指定的代码块,不满足的话就什么都不做。

  • if-else 语句: 判断一个条件是否满足,满足则执行 if 指定的代码块,不满足的话则执行 else 指定代码块。

  • if-elif-else 语句: 从 if 的条件开始,依次从上到下判断条件是否满足,直到遇到第一个满足的条件,遇到时就执行这个满足条件对应的代码块,如果所有条件都不满足的话,就执行最后 else 对应的代码块。

循环结构

现在咱们来看看代码结构的另外一个重要分支:循环结构。顾名思义,循环结构实现的就是重复执行同一个代码块多次,在很多场景都非常重要。基本上所有统计逻辑:均值、方差等背后,都是通过循环结构来实现的。

比如你写了一段分析单个用户下单习惯的数据分析代码,但你拿到的数据集里面有 10 万个用户,咱们肯定是不能一个个弄,横竖都弄不完,这里就需要一个强大的批量执行工具,也就是循环语句。

先从一个简单的场景开始思考,假设我们要打印三次:重要的事情说三遍。按照目前我们学习到的知识,最直接的就是直接写三次 print 语句。

print("重要的事情说三遍")
print("重要的事情说三遍")
print("重要的事情说三遍")

输出结果如下:

重要的事情说三遍
重要的事情说三遍
重要的事情说三遍

代码不难理解,但也显得非常冗余,是否有更好的方式呢?

for 语句

for 语句是 Python 实现循环的方式之一,也是最常用的方式,语句形式是这样的。

for 循环变量 in range(开始值,结束值,步长):代码块

结构看起来比较复杂,但其实形式比较固定,我们来看看其中的几个关键元素。

  • for..in..: ,是 python 的语句,代表循环,因为需要控制一个代码块,所以和 if 语句一样,末尾需要加冒号。

  • 循环变量、开始值、结束值、步长:用来控制循环的次数,循环变量的变量名没有特殊要求,和普通变量一样。步长是一个新概念,通俗理解就是“步子的长度”,在这里的意思就是循环变量每次增加的值,默认是 1。

  • 代码块:每次循环时执行的代码块,它和由 if 语句控制的代码块一样,这个代码块由上面的 for 语句控制,所以也需要缩进。

这个结构做了一件什么样的事情呢?用白话来说就是,从循环变量=开始值开始,循环执行代码块,每次执行,循环变量的值都+步长,当循环变量的值大于或者等于结束值的时候,退出循环。

我们通过代码举例来理解。

for i in range(1,10,1):print(i)

在这个例子里,循环变量 = i,开始值 = 1, 结束值 = 10, 步长 = 1。

结合我们的上面的描述,这个循环的逻辑就是变量 i 从 1 开始循环,每次 +1, 当超过 10 时停止,所以循环一共会执行 9 次,每次循环的代码块都会打印变量 i 的值。

我们运行代码后可以看到这样的输出结果。

1
2
3
4
5
6
7
8
9

如果我们把步长改成 2 会怎么样呢?按照刚才的说明,则是变量从 1 开始,每次 +2, 当大于 10 的时候退出循环,听起来是不是会导致循环次数减半?我们来验证一下,代码如下。

for i in range(1,10,2):print(i)

输出结果确实如我们预期,只循环了五次,并且每次 i 都会 +2。

1
3
5
7
9

在我们日常写的代码中,最常用的步长就是 1,对于步长为 1 的循环,可以不写步长,因为 for 循环语句默认的步长就是 1,所以刚才第一个例子,我们也可以简写为这样。

for i in range(1,10):print(i)

学习了循环之后,我们来尝试计算一个经典问题,计算 1+2+3...+99+100。当初高斯通过取巧的方式快速计算出了结果,现在我们尝试使用 Python 来实现。

问题分析:我们需要计算 100 个数的和,这么多数显然是不能都写出来的,回过头看刚才我们循环打印的值,可以发现,其实我们通过循环,可以让一个变量从 1 循环到 100,每次 +1。然后我们只需要在循环的代码块中把循环变量都加起来,就可以得到结果。

另外,还有一点需要注意,因为需要加到 100,而我们判断是否退出循环的标准是:循环变量是否大于等于结束值,所以我们循环的结束值应该是 101。

新建 Cell,输入如下代码。注意在这个例子里,因为我们需要累加,所以需要声明一个新的变量,我们命名为 result。

result = 0
for i in range(1,101):result = result + i
print(result)

输出如下:

5050

可以看到,我们用 Python 计算的结果和高斯计算的结果是一样的。

while 语句

Python 还有另一种实现循环的语句——while 语句,相比 if 语句形式上更简单,也更通用。它的语句形式是这样的。

while 循环条件:代码块

是不是很简单?关键要素主要有这两个。

  • 循环条件和 if 语句的条件一样,是一个布尔变量或者以布尔类型为结果的表达式,比如 "a > 3""True" 等都可以作为条件。只是这里的条件是用来判断是否继续循环。

  • 代码块,循环时候执行的代码块,受 while 语句控制,需要缩进。

while 语句的核心流程就是,当条件为真时,不断循环执行代码块,直到条件为假。这里的 while 其实翻译过来,就是 “当”的意思。

现在,我们尝试用 while 语句来实现一下刚才计算 1+2+3+...+100 的问题。

问题分析:

刚刚我们用 for 循环来计算这个问题的实现方式,是用一个变量 result 来累加循环变量 i 的值,然后指定循环变量 i 从 1 循环到 100 即可。这样第一次循环,i 是 1,第二次循环 i 是 2,以此类推。

这次我们用 while,没有了循环变量,没有东西给我们累加到 result 上了,怎么办呢?我们可以自己声明一个普通的变量,这个变量随着循环的次数增加,值也相应增加。类似上面的 i,第一次循环是 1,第二次循环是 2..一直到第 100 次循环是 100,具体在 while 循环中实现这个过程,我们只要在代码块中每次给这个变量 +1,然后再把这个变量持续累加到 result 变量即可。

然后当循环变量超过 100 时,退出循环,那么我们可以用 上述变量 < 101 作为循环条件。

我们来实操下,新建 Cell ,输入如下代码:

i = 0
result = 0
while i < 101:result = result + ii = i + 1
print(result)

输出如下,可以看到,和 for 语句实现的版本是一致的。

5050

这个部分有点绕,不用着急,多看多想几次,再对比上面 for 循环的实现,就能理解。

死循环

相信不少同学看到 while 语句的形式就会想到一个问题,如果条件一直都为 True,那循环不是永远退不出来了?确实存在这种现象,在 Python 中,我们把这种循环一直无法退出的现象称之为:死循环。

死循环会导致程序执行卡死,因为一直在执行循环无法退出。所以在日常编程中,我们都需要尽量避免死循环,保证每个循环的退出条件都是可以达到的。

要测试死循环很简单,新建 Cell,输入如下代码

while 1 < 2:print("dead loop")

因为 1 永远小于 2,所以这段代码执行之后会无限输出 dead loop,在 Notebook 中执行如下所示,(注意左边的执行标志一直是 * 号,代表一直未执行完毕)。

当我们不小心执行了死循环之后,可以通过点击工具栏的停止按钮强制停止。

循环语句中的 break 和 continue

学完了死循环,想必你现在会有点紧张,感觉条件只要稍微不对就死循环了,那除了从条件入手,保证写了合适的条件之外,有没有其他避免死循环的方式呢?答案是肯定的,Python 的循环中有两种特殊的语句,用于终止循环。一种是 break 语句,用来终止整个循环。一种是 continue 语句,用于跳过当次循环。

(1)break 语句:结束当前整个循环语句的执行,转而执行循环语句后面的代码

以下面的代码为例:

for i in range(1,10):print("Hello")print(i)
print("loop finished")

根据 for 语句的定义,我们不难知道这段代码将会循环 9 次,每次都打印 Hello 和对应的循环变量的值,并在循环结束之后打印 loop,如下所示。

Hello
1
Hello
2
Hello
3
Hello
4
Hello
5
Hello
6
Hello
7
Hello
8
Hello
9
loop finished

我们在两个打印中间插入一个 break 语句,将代码改成这样,会发生什么事情呢?

for i in range(1,10):print("Hello")breakprint(i)
print("loop finished")

运行一次,可以看到输出如下:

Hello
loop finished

从结果上看,代码只打印了一次 Hello 便打印了 loop finished,甚至一次都没有打印变量 i 的值。这说明在循环内的代码块中遇到 break 时,会马上结束整个循环语句的执行,转而执行循环语句后面的代码。在这个例子里面,也就是打印 loop finished。

(2)continue 语句:结束当次循环的执行,转而执行下一次循环。

注意 continue 和 break 的区别,break 是结束整个循环语句的执行,而 continue 只是结束当次循环的代码块,并不会退出循环语句。我们可以基于上述例子的代码来理解,如果将上面的代码的 break 换成 continue:

for i in range(1,10):print("Hello")continueprint(i)
print("loop finished")

输出结果如下:

Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
loop finished

可以看到,打印了 9 次 hello,之后打印了 loop finished,同样一次都没有打印变量 i。原因就是每次执行循环代码块,执行到 continue 时,退出了当前代码块的执行(也就是没有执行到打印变量 i 这一句),执行了下一次循环,所以还是正常循环了 9 次。

break 与 continue 语句在 for 循环和 while 循环中的作用都是一样的

小结

今天,我们学习了 Python 中代码执行流程的基础:分支结构与循环结构。通俗来说,我们可以通过分支结构来实现代码的选择执行,用循环结构来执行代码的重复执行。

下面我们来做一个简单的回顾,分支结构中,有三个语句。

  • if 语句:判断一个条件是不是满足,满足的话就执行指定的代码块,不满足的话就什么都不做。

  • if-else 语句:判断一个条件是否满足,满足则执行 if 指定的代码块,不满足的话则执行 else 指定代码块。

  • if-elif-else 语句:从 if 的条件开始,依次从上到下判断条件是否满足,直到遇到第一个满足的条件,遇到时就执行这个满足条件对应的代码块,如果所有条件都不满足的话,就执行最后 else 对应的代码块。

循环结构中,有两个语句:

  • for 语句,就是让循环变量从开始值一直叠加,当大于等于结束值时结束循环,其中每次叠加都会执行一次代码块;

  • while 语句,while 语句就简单很多,判断条件是否满足,满足则执行代码块,执行完后再检查条件,满足的话就继续执行,周而复始,直到遇到条件不满足则退出循环。

两个循环语句都有一些共同的特性,比如条件不对时可能会导致死循环,不过都可以通过 break 语句退出循环,或者通过 continue 语句跳过当次循环。下面我们再复习一下这几个特性:

  • break 语句,用于强制退出整个循环语句的执行;

  • continue 语句,用于退出单次循环的执行,转而执行下一次循环;

  • 死循环,当循环条件一直为 True 时,循环无法退出导致程序无限执行循环的代码块的现象,要尽量避免。

分支和循环结构是 Python 程序的核心,也是绝大多数“逻辑”的实现方式,所以需要你仔细理解。尤其是循环,对于很多没有接触过编程的同学来说,循环的实现方式并不是非常符合直觉,但不要担心,只要多想多试多练习,你很快就会豁然开朗。

课后练习

  1. 刚才我们通过 for 循环打印了数字 1~9。结合本讲学习的内容,尝试使用 for 循环或者 while 循环打印以下内容。

9
7
5
3
1
  1. 结合本讲学习的内容,尝试使用 for 循环或者 while 循环计算 6 的阶乘,也就是计算 6 * 5 * 4 * 3 * 2 * 1 的值。

如果你做出来了,欢迎把你的代码分享在评论区!


03 函数:快速实现代码的复用

上一讲中,我们学习了代码的分支结构(if 家族语句)和循环结构(for 循环和 while 循环)。通过学习这些结构,我们已经能够写出稍微复杂一些的代码。但当代码一多,就会遇到一些问题。

还记得上节课我们写过一段,根据安全考试分数打印安全意识分级(优秀、及格和不及格)的代码,如下所示:

a = 75
if a > 80:print("优秀")
elif a > 60:print("及格")
else:print("不及格")

如果我们现在需要一次性看三位同事的分级结果,比如 a 同事 75分,b 同事 90 分,c 同事 66 分,然后打印出这三个同事每个人的分级信息。根据我们之前学习的知识,很自然就想到,针对每个变量都执行一次上面的 if-elif-else 语句即可,完整的代码是这样的:

a = 75
b = 90
c = 66
if a > 80:print("优秀")
elif a > 60:print("及格")
else:print("不及格")
if b > 80:print("优秀")
elif b > 60:print("及格")
else:print("不及格")
if c > 80:print("优秀")
elif c > 60:print("及格")
else:print("不及格")

最终输出是及格、优秀、及格,分别代表 a、b、c 三位同事的分级结果。

及格
优秀
及格

结果是计算出来了,但回过头去看会发现明明看着很像,却愣是重复写了三次。有没有更好的方式来避免重复写多次结构类似的代码呢?而且这个例子,重复的部分还只有6 行,要是一个计算过程有 50 行,然后要针对一个部门 50 个同事都执行一次,难道我们需要写 50 x 50 = 2500 行代码吗?

答案是肯定的,在 Python 中相似结构的代码复用通过函数来实现。学习了函数,我们才能真正意义上编写复杂的代码。

接下来,我就为了详细的介绍它。Let's go go go~~~

什么是函数

看到函数,可能你首先会条件反射地想到数学中的“函数”,但 Python 中的函数和数学中的不是一回事,不需要联合起来理解。

Python 中的函数简单来说就是一段有名字的代码块。通过函数的机制,我们可以给我们希望重复使用的代码块起个名字,这样我们之后要用这个代码块的时候,就不需要重新写一遍一模一样的代码块,而只需要简单写一次之前给代码块起的名字即可。普通函数的形式如下所示:

def 函数名():代码块

其中的关键要素是:

  • def, Python 的关键字,代表我们接下来要创建一个函数;

  • 函数名,顾名思义,函数的名字,名字需要符合变量起名的定义;

  • 冒号,大家应该很熟悉了,这代表冒号后面的代码块的名字是冒号前面的【函数名】;

  • 代码块,也就是我们想起名字来指代的代码块,这种函数对应的代码块,我们也叫函数体。注意这里的代码块和 if 语句的代码块一样,需要缩进。按照Python 的规则,在冒号后面的代码块都需要相对于冒号所在的行缩进

当我们创建完函数之后,要实际执行函数中的代码块,还需要执行该函数。函数执行的形式比较简单:函数名之后加一个括号即可。形式如下:

函数名()

小实战:打印字符三角形

现在我们用一个简单的例子来感受函数的用处。假设我们需要使用字符 A 打印一个简单的三角形。

print("  A")
print(" AAA")
print("AAAAA")

输出

  AAAA
AAAAA

现在我们想打印三个,虽然我们可以用类似本文开头的方式,把上面的代码复制出三份。但现在我们用函数的方式来解决。新建 Cell ,输入如下代码:

def print_triangle():print("  A")print(" AAA")print("AAAAA")

我们按 shift+enter 执行上述代码,发现并没有任何内容输出。原因就是目前我们只是创建了函数(给代码块起了名字),但还没有实际执行它。

现在我们来执行一下,新建 Cell,输入以下代码:

print_triangle()

执行代码,输出结果就可以看到我们的代码块被成功执行了。

  AAAA
AAAAA

讲到这里,相信你已经知道怎么用更方便的方式打印三个三角形了,答案就是执行三次 print_triangle 函数。

print_triangle()
print_triangle()
print_triangle()

输出是这样的:

  AAAA
AAAAAAAAA
AAAAAAAAA
AAAAA

通过上述例子可以看到,我们可以通过函数的形式给代码块起一个名字,之后只需要在代码中执行这个函数就可以起到和执行代码块一样的效果。这样的实现方式可以减少整体的代码量,也能够让代码整体更加清晰。

函数的参数

有“变化”的变量

现在我们回过头去看开篇遇到的给三个同事的分数分级的问题。我们之前通过三个 if-elif-else 语句来实现,现在尝试使用函数来优化这个代码。初步形式如下所示:

def print_level():if a > 80:print("优秀")elif a > 60:print("及格")else:print("不及格")

细心的你很快就发现了问题……这个函数每次都只检查了 a 同事的分数,但我们的任务是要分别检查 a、b、c 三个同事的分数,怎么办?

换句话说,我们希望打印 a 同事分级时,这段代码的 if 语句 判断的是 a,打印 b 同事的分级时,这段代码的if 语句判断的是 b。简单来说, if 语句判断的这个变量,不能在代码块中把他写死,而是应该在执行函数的时候传进来。

在 Python 中,我们可以使用函数的参数来解决这个问题。我们可以把需要执行阶段传入的变量写在函数名后面的括号中,需要几个写几个。这样的变量就被称为参数。形式如下:

def 函数名(参数1, 参数2, ...):代码块

之后,在代码块中,我们可以直接使用参数 1、参数 2 这些变量。以上面的例子来说,if 语句就不用再直接判断 a,而是判断参数的值。

创建完了函数,执行的时候怎么传入具体的值呢?很简单,只需要在执行函数的时候,在函数名后的括号中写上具体的变量就好了。形式如下:

函数名(参数1, 参数2, ..)

老规矩,我们举一个非常简单的例子加深理解。假设我们编写一个函数:打印某个数 +3 的结果。这里的“某个数”,我们用参数传入。

函数定义:

def print_out(a):print(a + 3)

print_out(3)

输出

6

我们来剖析这里面发生了什么:

  • 当函数执行到 print_out(3),发现这里是执行了一个函数,并且要把 3 传给这个函数;

  • 下一步,找到print_out 函数的定义(第一行),把数字 3 赋值给参数列表的a;

  • 下一步,执行函数里的代码块,也就是print(a + 3), 此时 a 已经等于外部传入的3,所以输出 6。

使用函数参数改造分级函数

相信你看到这里,可能对于函数参数到底是怎么帮我们解决问题的,还有点懵。

我们还是通过例子来理解,我们希望 print_level 函数能够根据需要处理不同的分数,比如第一次执行,处理的是 a 同事的分数,第二次是 b 同事。那么我们可以把对应代码块中的变量变成参数,假设取名为 score。

新的函数代码如下:

 def print_level(score):if score > 80:print("优秀")elif score > 60:print("及格")else:print("不及格")

与之前实现的主要区别就是,我们不再直接判断某个同事的分数(比如之前的变量 a)。而是在函数名的括号中声明了一个参数score,之后在函数体中就直接针对 score 进行判断。

然后执行上述代码,确保函数创建成功。

接下来就到了函数执行的环节,我们希望可以用该函数分别处理三个同事的分数,那是不是就是执行三次函数,然后每次的参数就写对应 a、b、c 同事的分数就可以了呢?你可以先思考一下,然后再看实现的代码。

a = 75
b = 90
c = 66
print_level(a)
print_level(b)
print_level(c)

输出可以看到,执行的结果和我们写三个很长的 if-elif-else 语句是一样的。

及格
优秀
及格

上例中的 score,我们称之为形式参数,也叫形参。形式参数顾名思义,是声明了一个变量,给函数体中写逻辑用的,上例中我们的逻辑就是通过对形式参数 score 进行判断写的。

上例中的 a、b、c,我们称之为实际参数,也叫实参。实际参数就是指函数实际执行的时候,函数的形式参数的值。比如当执行 print_level(a) 时,这个时候执行到函数里面,形式参数 score 的值就等于 a ,也就是 75。

小实战:打印员工信息

我们再通过一个例子加深印象。假设我们需要编写一个函数,实现打印员工的基本信息。格式如下

姓名:小明
年龄:12
编制:正式员工

通过分析可以发现,打印前缀的提示,比如“姓名”“年龄”这些是不变的,而具体每个员工的性别、年龄以及编制信息都是会随着员工不同而变化的。那显而易见,这里我们需要三个参数。你可以先尝试自己写一下,然后再来看我的方式。

def print_info(name, age, title):print("姓名:" + name)print("年龄:" + str(age))print("编制:" + title)

上述代码有几个注意的点。

  • 参数的类型,name、title 是字符串类型,age 是整数类型,还记得我们在变量与数据类型一章学过,不同类型的变量做运算的时候需要先进行类型转换。这里也一样,整数和字符串不能直接拼接,需要先将整数通过 str 函数转换为字符串。

  • 我们不能直接让字符串和整型相加,所以我们通过 str 函数来将整型 age 转换为字符串再相加

执行上述代码,并新建 cell,添加下面的代码测试一下。

print_info("小明",30, "正式员工")
print_info("小红",21, "实习生")

输出如下:

姓名:小明
年龄:30
编制:正式员工
姓名:小红
年龄:21
编制:实习生

可以看到我们通过两次调用 print_info 函数,并传入不同的信息,实现了打印不同员工信息的功能。
总结一下,当函数的代码需要处理每次执行都可能会变化的变量时,可以将这些变量声明为形参,放在函数名后面的括号里。然后在函数实际执行的时候,根据我们希望函数处理的变量以实参的形式传递给函数。

函数的返回值

通过函数的参数机制,本质上我们实现了可以向函数发送信息的本事(函数参数其实就是在函数在执行的时候,外部代码发送给函数内部代码的值)。另一方面,函数内部是否可以向外部代码发送信息呢?

从一个具体的例子来说,刚才我们打印了 a、b、c 三个同事的分级结果。现在需要编写函数,统计三个同事的考试分数的平均值。

问题分析:计算三个同事分数的平均值,本质上就是计算三个数字的平均值。那代表这个函数需要接受 3 个参数(因为可能换另外三个人,那三个成绩就都不一样了,所以在这里会有变化的变量有 3 个)。

另外,因为是求三个数字的平均值,我们就起个函数名叫:three_average。基于此,我们可以编写如下的函数:

def three_average(score1, score2, score3):result = (score1 + score2 + score3) / 3

代码也比较容易理解,我们声明了三个参数:score1、score2、score3 。然后在函数中我们计算了他们的均值并存储在变量 result 中。

老规矩,执行上面的 cell,并新建 Cell 输入以下的测试代码。

three_average(a, b, c)
print(result)

我们执行了three_avarage 函数,并将 a、b、c 三名同事的成绩作为实参传递给它,之后我们尝试打印出 result 变量的值。

运行代码,报了如下的错误:

---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-36-e489956d03fc> in <module>1 three_average(a, b, c)
----> 2 print(result)
NameError: name 'result' is not defined

错误提示说的是:变量名 result 未定义。
回过头去看 three_average 函数,我们的result变量是在函数内部被赋值的。所以在函数外面是用不了了。这里涉及一个新的知识点:在 Python 中,函数内部创建、赋值的变量,仅在函数内部有效。

那现在问题来了,我们计算的 result如何告诉给调用three_average的代码?这个问题如下所示

three_average(a, b, c)
print(???)  <---------这里该 print 什么?怎么拿到函数内部计算的均值?

return 语句

Python 中,通过 return 关键字来实现将函数内部的值返回给调用函数的代码。形式如下:

def 函数名(参数1, 参数2, ...):代码块return 返回值

新增了以下要素。

  • return,是一个 Python 的关键字,代表将后面的值作为该函数的返回值返回给调用方。另外,return 语句一般写在最后,因为 return 除了返回值之外,还会终止当前函数的执行。类似 for 循环中的 block。简单来说,在函数体中所有在执行return之后的语句都不会被执行。

  • 返回值,在 return 语句后面,代表函数需要返回给外部的值,返回值可以是变量,比如上面例子的 result,也可以是一个常量,比如数字 3、字符串 “Hello” 等。

函数内部通过 return 语句返回了内容,执行函数的时候怎么拿到呢?我们可以在执行函数的时候,将执行函数的代码放在等号的左边,右边是一个变量,这样就实现了把函数的结果,赋值给了一个变量。

形式像这样:

变量 = 函数名(参数1, 参数2,...)

现在,我们使用 return 语句来解决我们在本章开头的问题,重写 three_average 函数。

def three_average(score1, score2, score3):result = (score1 + score2 + score3) / 3return result
final_score = three_average(a, b, c)
print(final_score)

新版本主要有以下改动点:

  • 通过 return 语句,把 result 变量返回给外部;

  • 外部调用的时候,我们把函数返回的结果赋值给变量 final_score;

  • 打印出 final_score 的值。

输出结果为 77。

77.0

大功告成,我们的 three_average 函数在得到 return 语句的加持之后,才能变成真正意义上好用的函数。

小实战:编写获取员工分级的函数

刚才我们的例子中已经实现了打印员工分级的 print_level 函数,假如我们不希望在函数里面直接打印,而是通过一个函数,把分数转换成分级信息返回给外部,该怎么做呢?

在思考一个函数该如何实现的时候,我们可以遵循以下的三段论。

  • 明确需求:就是函数具体要做一个什么事情,这就要求我们理解任务/需求的描述。

  • 明确输入:就是想清楚函数有哪些参数,扩展来说就是函数要完成上面的任务,哪些变量是需要从外部传入的。

  • 明确输出:就是函数需不需要返回值,以及返回值是什么,这同样取决于第一步,需求是什么。比如我们只是想让函数内部打印一些内容,那一般不需要返回值。而如果我们希望函数能够做一些计算,并将结果告诉调用方时,则需要返回值。

现在我们用三段论来思考一下这个实战。

  • 明确需求:把员工的分数转换为分级,并将分级返回给外部,不用打印。

  • 明确输入:员工的分数。

  • 明确输出:分级的结果。

根据上面的分析,以及return 语句的学习,很容易想出我们只需要将最开始的 print_level 的实现中,打印的代码换成 return 语句即可实现将分级结果返回给外部。另外,由于我们并不会打印分级信息而是返回,所以我们新的函数取名为 get_level。实现如下:

def get_level(score):if score > 80:return "优秀"elif score > 60:return "及格"else:return "不及格"

接下来编写代码来调用 get_level,测试一下功能是否正常

a = get_level(50)
b = get_level(65)
c = get_level(90)
print(a,b,c)

输出

不及格 及格 优秀

说明我们 get_level 函数被成功执行,并返回了分级的结果,分别存储在了 a,b,c 三个变量上。

Python 的库函数

关于 Python函数的关键特性在上面已经基本介绍完毕了。Python 代码中,我们一般主要打交道的函数有两种类型:

  • 库函数,已经由 Python 的维护人员创建好的,我们可以直接使用的函数;

  • 用户自定义函数,由我们自己创建的函数。

很明显,我们在这一课中定义的 print_level、print_info以及 three_average,list_average函数都属于用户自定义函数。而我们一直以来使用的:

  • 打印信息倒屏幕的print语句;

  • 计算字符串和列表长度的 len语句。

本质都是 Python的库函数。

现在我们就用函数的视角,来分析一下len 和 print 的功能

  • len 函数:接收一个参数,类型是列表或者字符串,返回值为列表和字符串的长度。

  • print 函数:接收一个或多个字符串,把他们打印在屏幕上,没有返回值。

Python 的库函数非常多,感兴趣的同学可以到
Python 的官方文档库浏览学习,但不是要求,因为大部分在本篇教程都用不到。而且学了用不上也很容易忘,所以后续对于 Python 的库函数我们以够用就好的原则进行讲授,要用哪个就教你哪个。

小结

今天,我们学习了 Python 中代码复用的主要手段,也是绝大多数 Python 程序的核心组成部分:函数。

总结下来便是这几个问题。

(1)函数是干啥的?

我们可以通过函数的形式给代码块起一个名字,之后只需要在代码中执行这个函数就可以起到和执行代码块一样的效果。

(2)函数的参数

当函数中用到的变量需要从外部传入时,这样的变量声明为参数。参数主要是为了让函数变得更加通用。

在调用函数时根据需求传入不同的参数,使得同一个函数可以处理多个任务。比如打印员工分数分级的例子,我们把分数变为参数后,同一份函数代码可以用来打印 a 同事的分级,也可以用来打印 b 同事和 c 同事的分级,只需要多次调用函数即可,不需要多次编写函数代码。

(3)函数的返回值

当函数需要把内部的结果告诉给外面的调用方时,可以通过 return 语句来实现。return 语句后面的内容,我们称为这个函数的返回值。通过函数的返回值最大的应用场景就是可以帮助我们编写各类数学计算函数(比如统计平均值等)和数据转换函数(比如分数转分级等)。

函数是非常重要的概念,也是现实世界中 Python 程序的重要组成部分。多花点时间,好好吃透它!

课后练习:

  1. 在上一讲中,我们通过 for 循环实现过 1+2+3+4...+100,现在尝试实现更通用的方案。

    1. 编写函数,实现计算 1+2+3+4...+N,N 作为函数参数由外部传入,返回求和之后的结果(记得借助三段论的思想进行推导)。

    2. 编写测试代码,调用上面的函数,计算 1+2+3...+49 的结果。


练习答案:

# 1.a
def accumulate(n):result = 0for i in range(0,n+1):result = result + ireturn result
# 1.b
a = accumulate(49)
print(a)

04 类与对象:更好地组织你的代码

上一节,我们学习了函数。这一讲,我们来学习 Python 中另外一个重要的概念:类与对象。

类与对象

类与对象有什么关系

你可能会奇怪,为什么要叫类与对象呢?是两个不同的东西吗?简单来说,类代表一个类别,而对象则代表类的一个实例。比如我们在变量与数据类型中学习的整型变量为例。

a = 3
print(type(a))

输出结果为:

<class 'int'>

我们创建了一个变量 a,并赋值为 3,然后打印了它的类型。可以看到,输出 int,代表整型变量。
细心的你也发现了,输出里面有一个单词 class,它就是类别的意思。难道说?所谓的整型,是一个类?你猜得没错。这里的整型:int,就是一个类。而我们创建的整型变量 a,就是他的一个实例。

我们再举一些生活中的例子:

  • “人”是一个类,小明则是“人”这个类的一个实例,所以小明是一个对象;

  • “打车公司”是一个类,滴滴则是这个类的一个实例对象(也可以隐藏 实例 这个称呼);

  • “酱油”是一个类,海天酱油 则是一个对象;

  • “车”是一个类,奔驰车则是一个对象。

细心的你不难发现,类与对象本质上是抽象与具象的关系,对象在类的基础上进行了适当的具象。所以在某个抽象关系中的对象也可能会成为另一个抽象关系中的类。比如上面奔驰车是车的一个对象,那同样可能存在,奔驰车是一个类,而 S350L 是一个对象。

Python 中的类

理解了类与对象,现在我们来看一下 Python 中的类,我们在开头的例子中提到,类是用来把有联系的数据和函数给组织起来的一种方式。

在类中,数据被称为属性,而函数则被称为方法。每个类都可以有零个或者多个属性,也可以有零个或者多个方法。

类的属性和方法的概念也很适合用来描述现实世界,比如“人”这个类,身高、体重、年龄等就是属性,而走路、吃饭、打球 等动作就是方法。

接下来,我们通过一个例子来演示怎么定义类,怎么使用类。

定义一个“人”的类

现在我们尝试定义一个人的类。需要有年龄、性别、姓名三个属性,并提供两个方法:introduce 方法,打印一句介绍自己的话。get_age 方法,返回当前对象的 age。

# 类的定义环节
class Person:def __init__(self):self.name = ""self.age = 0self.gender = ""

def introduce(self):
        print(“Hello, 我是” + self.name)
        print(“我今年 “ + str(self.age) + ” 岁”)
        print(“另外, 我是” + self.gender)

def get_age(self):
        return self.age
# 使用上面定义的类
xm = Person()
xm.name = “小明”
xm.age = “25”
xm.gender = “男生”
print(xm.get_age())
xm.introduce()

简单说一下上面的代码的主要逻辑,你主要看代码就好。

  • 在定义环节:

    • 定义了一个类:Person(类名首字母用大写),并用 init 函数中初始化了三个属性:name,age,gender。(init 函数,前后都有两个下划线,代表初始化函数。简单来说就是,当我们创建这个类的对象的时候,这个函数会被自动执行,不用你再shift+enter。)

    • 定义了 introduce 方法,会把几个属性的值组合成一段自我介绍,这里我们通过方法参数 self.属性名 的形式来拿到类的属性的值。这种形式叫作**点语法。**点语法的本质就是找点前面的对象拿点后面的属性的值。比如 A.B 代表 A对象的B属性的值。

    • 定义了 get_age 方法,把当前对象的 age 对象的值返回。可以看到,无论是 introduce 方法 还是 get_age 方法,都有一个 self 参数。这是 Python 的语法规定,类的方法的第一个参数都必须是 self。这样方法内部就能通过对 self 使用点语法来获取属性的值。

  • 使用环节:

    • 注意从我们使用的类的代码开始,我们这里就没有缩进。因为有缩进就会被认为还是在类的内部,但这里的代码是在外部的,所以不用缩进;

    • 我们创建了一个 Person 对象,存在变量 xm 中,之后逐一给它的属性赋值

    • 打印 xm 对象的 get_age 方法返回的值

    • 调用 xm 对象的 introduce

运行后,输出如下所示:

25
Hello, 我是小明
我今年 25 岁
另外, 我是男生

可以看到,通过类的机制,我们成功地把数据和函数绑定在了一起。比如上面例子的数据:name、age、gender 和函数:get_age、introduce。并且类提供了一种机制(self 参数),能够让类的方法可以访问类的属性。

给属性赋值

回看刚刚的例子,我们创建了 Person 类的对象之后,逐一对他的属性赋值,每一次属性赋值都需要针对 xm 对象使用点语法,比较麻烦,有没有更好的方式呢? 答案是肯定的。

我们稍微改造一下刚才的例子,改造后的代码如下所示:

class Person:def __init__(self, name, age, gender):self.name = nameself.age = ageself.gender = gender

def introduce(self):
        print(“Hello, 我是” + self.name)
        print(“我今年 “ + str(self.age) + ” 岁”)
        print(“另外, 我是” + self.gender)

def get_age(self):
        return self.age
xm = Person(“小明”, 25, “男生”)
print(xm.get_age())
xm.introduce()

执行之后,输出和刚才是一致的。

25
Hello, 我是小明
我今年 25 岁
另外, 我是男生

不同在哪儿呢?是这里。

现在我们不需要进行逐个属性的赋值,而是在构造对象的阶段就完成了几个属性的初始化。

这其中的奥妙就在 init 方法里,我们给 init 函数加了参数,然后用这些参数直接在 __init__的函数体中给我们的属性赋值。这样直接在创建对象的时候,把对应的值依次放在类名后的括号中,用逗号分隔。就能实现一次性地给属性都赋值,精简了代码。

常见的系统类

类和函数一样,类同样也有 Python 的开发者们提前写好提供给我们使用的类,一般称为系统类。上面举的例子都是我们自己实现的类,本节会重点介绍常用的系统类。

列表(list)

列表相信大家都不陌生,在变量与数据类型一章已经介绍过,它是 Python 非常常用的数据类型。

这里介绍一下列表的基础使用,代码如下:

# 创建列表
a = [90,1,23, 15]
# 访问某个元素(下表从 0 开始)
print("Third number is ",a[2])
# 使用 append 方法添加元素
a.append(-1)
print("After append: ", a)
# 删除某个位置的元素
del a[1]
print("After delete second number: ", a)
# 删除具体某个元素
a.remove(15)
print("After delete 15:", a)
# 排序,默认从小到大
a.sort()
print("After sort", a)
# 逆排序,从大到小
a.sort(reverse = True)
print("After reverse sort", a)
# 求长度
print("Length: ", len(a))

上面主要方法都有注释,这里不再展开,你可以结合以下输出和代码,加深理解。重要的是你要知道,如果之后让你做某个事(删除、排序等),你能直接使用 list 类对应的方法。而不需要自己写循环完成。

Third number is  23
After append:  [90, 1, 23, 15, -1]
After delete second number:  [90, 23, 15, -1]
After delete 15: [90, 23, -1]
After sort [-1, 23, 90]
After reverse sort [90, 23, -1]
Length:  3

字符串(string)

字符串我们用的也是非常多的,在之前也介绍过一些基础用法,比如用 + 号来连接两个字符串,这里我们再介绍额外的一些用法。

a = "Hello,this is my home,welcome"
# 添加字符
a = a + ",pp"
print("\nAfter append pp:\n", a)
# 删除前 x 个字符,比如 3
a = a[3:]
print("\nAfter remove first 3 characters:\n", a)
# 删除固定内容的字符
a = a.replace("my","")
print("\nAfter remove 'my':\n", a)
# 替换固定内容成另外一个字符串
a = a.replace("home", "company")
print("\nAfter replace 'home' to 'company':\n",a)
# 用某个字符分割字符串,返回一个数组,比如逗号
str_list = a.split(",")
print("\nList split from string by comma:\n", str_list)

输出

After append pp:Hello,this is my home,welcome,pp
After remove first 3 characters:lo,this is my home,welcome,pp
After remove 'my':lo,this is  home,welcome,pp
After replace 'home' to 'company':lo,this is  company,welcome,pp
List split from string by comma:['lo', 'this is  company', 'welcome', 'pp']

字典(dict)

字典和列表、字符串一样,也是 Python 中相对常用的数据类型,同样也是系统类。因为复杂一些,所以在变量与数据类型一节没有介绍。

字典和列表类似,也是存储多个变量的容器。但与列表不同的是,字典存储的不仅仅只有变量,还有变量之间的映射关系。

我们举个例子,假设我们要存储部门里三位技术顾问的年龄,可以用列表来完成,比如 a = [30, 45, 50], 但如果我们不仅要存储年龄,还得存储这三位工程师的名字。那用列表就无能为力了。

简单来说,我们希望存储一个名字-年龄的映射关系。在这个例子上就是要存储这种形式的数据:王叔:30,张叔:45,李叔:50。在 Python 中,这种有对应关系的两个变量我们称之为键值对。比如王叔就是 键(key),而 30 就是值(value),一个键会唯一对应到一个值。

这里我们希望存储的就是三个键值对。而字典,就是专门用来存储键值对的容器。

我们以下面的例子来演示字典的使用,结合列表的例子可以帮助你快速理解。

# 创建一个空字典,用 花括号 {}
d = {}
print("Empty dict:", d)
# 用键值对创建字典,键和值中间用冒号隔开,不同的键值对用逗号隔开
d = {"xiaoming" : 3.5 , "xiaohong": 4}
print("Two key-value pair dict:", d)
# 访问字典的元素,和列表一样用中括号,但传入 key,查询 value。比如查询 xiaoming 的司龄
print("Xiaoming's company age:", d["xiaoming"])
# 添加新的键值对,直接对 key 对应的 value 赋值即可
d["xiaogang"] = 5.5
print("After append xiaogang:", d)
# 删除键值对,类似列表删除,只是中括号内写 key,而不是序号
del d["xiaohong"]
print("After remove xiaohong:", d)
# 修改键值对,同添加一样,直接对某个已有的 key 重新赋值即可
d["xiaoming"] = 9.3
print("After change xiaoming's value:", d)

输出

Empty dict: {}
Two key-value pair dict: {'xiaoming': 3.5, 'xiaohong': 4}
Xiaoming's company age: 3.5
After append xiaogang: {'xiaoming': 3.5, 'xiaohong': 4, 'xiaogang': 5.5}
After remove xiaohong: {'xiaoming': 3.5, 'xiaogang': 5.5}
After change xiaoming's value: {'xiaoming': 9.3, 'xiaogang': 5.5}

实战类与对象

通过一段时间的学习,你成功拿到了阿普尔星球最大的电商网站:阿普闪购的 OFFER!成为一名助理数据分析师。入职第一天,你的 mentor 交给你一项任务:记录公司部门的信息,每个部门都需要统计部门名称、员工列表、部门主管的姓名等信息。另外还有两个要求:

  • 部门员工入职和离职都能方便的更新信息;

  • 可以方便地查看某个部门的汇总信息。

问题分析:

  • 目前我们需要记录的信息都是围绕部门这个实体的,所以我们可以用一个部门的类来记录,部门名称、员工列表和部门主管则都是这个类的属性。

  • 另外的要求是有人员变动的时候方便更新信息,那其实就是部门类需要提供增加人员和减少人员的方法。

  • 最后一个要求是方便查看部门汇总,那其实就是需要一个打印方法,来把部门的属性都打印出来。

我们接下来一步步来实现它。

(1)创建部门类

我们首先创建部门类和它的属性,部门名称和主管姓名,是字符串类型的变量,这两个属性我们通过初始化函数的参数来初始化。而员工列表是一个列表类型,我们先把它初始化成一个空列表。

class Department:def __init__(self, dep_name, boss_name):self.dep_name = dep_nameself.boss_name = boss_nameself.stuff_list = []

其中:

  • dep_name 是部门的名字

  • boss_name 是部门主管的名字

  • stuff_list 是员工列表,我们将其初始化为一个空列表。

要创建一个部门类的对象,只需要在类名后面传入具体的部门名和主管名即可。比如:

it_dep = Department("it部", "小明")

(2)实现增加员工的方法

目前员工列表,也就是 stuff_list 属性还是空列表,为了实现往这个列表增加员工的名字,我们需要在部门类增加一个添加员工的方法,我们就命名为 add_stuff。这个方法除了类方法必须带的 self 参数之外,只有一个参数:要添加的员工的姓名。

然后方法里面只需要把这个姓名添加到 stuff_list 即可,添加数据到列表,只需要调用列表对象的 append 方法即可。

根据上述分析,我们给 Department 类增加 add_stuff 方法。添加方法之后的类代码如下所示:

class Department:def __init__(self,dep_name, boss_name):self.dep_name = dep_nameself.boss_name = boss_nameself.stuff_list = []# 新增代码def add_stuff(self, name):self.stuff_list.append(name)

(3)实现删除员工的方法

部门会新增员工,比如员工入职,或者调入。也可能会减少员工,比如员工离职,比如转岗去其他部门,所以我们同样需要一个删除员工的方法。

类似添加员工的设计,删除员工的方法同样需要员工姓名的参数,在方法内部调用 stuff_list 对象的 remove 方法来将员工从列表中移除。

添加之后的类代码如下所示:

class Department:def __init__(self,dep_name, boss_name):self.dep_name = dep_nameself.boss_name = boss_nameself.stuff_list = []def add_stuff(self, name):self.stuff_list.append(name)

# 新增代码
    def remove_stuff(self, name):
        self.stuff_list.remove(name)

(4)添加打印部门信息的方法

打印部门信息,就是将部门的三个属性直接打印出来,比较简单,这里我们直接实现:

添加之后的类代码如下所示

class Department:def __init__(self,dep_name, boss_name):self.dep_name = dep_nameself.boss_name = boss_nameself.stuff_list = []def add_stuff(self, name):self.stuff_list.append(name)

def remove_stuff(self, name):
        self.stuff_list.remove(name)

# 新增代码
    def print_dep_info(self):
        print(“部门:”,self.dep_name)
        print(“主管:”,self.boss_name)
        print(“员工:”,self.stuff_list)

(5)使用部门类

现在,我们的部门类已经开发完毕了,现在让我们来用一用它,看看它是不是好使。

首先是记录部门信息,假设现在先记录2个部门:it部和财务部。首先要分别创建两个部门的对象:

it_dep = Department("it部", "小明")
finance_dep = Department("财务部", "红姐")

然后,通过调用部门对象的 add_stuff 方法录入员工的名字

it_dep.add_stuff("小亮")
it_dep.add_stuff("小七")
finance_dep.add_stuff("小E")
finance_dep.add_stuff("小洁")

现在,我们部门数据的记录就完成了,可以通过调用部门对象的 print_dep_info 来查看部门的详细信息,如查看 It 部的信息:

it_dep.print_dep_info()
finance_dep.print_dep_info()

执行后输出

部门: it部
主管: 小明
员工: ['小亮', '小七']
部门: 财务部
主管: 红姐
员工: ['小E', '小洁']

最后,当有人需要离职时,只需要调用 remove_stuff 方法即可,假设 it 部的小七决定去创业,要离职,我们需要将他从部门列表中移除。

it_dep.remove_stuff("小七")

为了查看是否成功移除,我们再次调用打印部门信息的方法。

it_dep.print_dep_info()

输出

部门: it部
主管: 小明
员工: ['小亮']

可以看到,小七已经不在员工列表中了。

至此,我们通过类与对象的方法,完成了部门信息统计的任务,并且可以非常方便地处理员工增加和减少的场景。

小结

总结一下,今天我们学习了 Python 除函数之外另一个重要的概念:类与对象。类通过属性和方法来将数据和代码组织在一起,对象则是类的实例。

举例来说,“人”是一个类,对于这个类而言:身高、体重、年龄是属性,走路、吃饭、睡觉是方法。一般来说名词多半是属性,而动词多半是方法。“小明”则是人这个类的一个对象。

之后,我们学习了 Python 中类的定义,主要有以下几个关键点:

  • 所有方法都有 self 参数,一方面这是 Python 的语法规则,另一方面方法中访问属性也需要用到 self;

  • init 方法中,需要给属性赋值,避免后续使用属性的时候属性还没有值,导致程序出错;

  • 可以给 init 方法增加参数,来让构造对象的时候就传入初始化属性的值,这样就可以避免在创建对象之后还逐个属性赋值,造成代码的冗余;

  • 使用类时,通过 对象 = 类名(参数...) 的形式构造对象,这是 Python的语法规则,照做就好;

  • 通过点语法来访问对象的属性或者执行对象的方法,同样,这也是 Python 的语法规则。

然后,我们学习了 Python 常见的三个系统类的基本用法。

  • 列表:按顺序存储多个元素的类。

  • 字符串:按顺序存储多个字符的类。

  • 字典:存储对应关系(键值对)的类。

最后,我们通过一个统计部门信息的实战案例,练习了类与对象的使用方法和技巧。

至此,我们整个 Python 语言的理论知识部分就学习完毕啦! 恭喜你,下一章,我们即将进入实战 Python 语言的环节,你是否已经摩拳擦掌了呢!别慌,先完成课后练习。

课后练习

编写一个车的类,包含属性:公里数、油耗、马力。

包含方法:

  • introduce,打印出车辆的基本信息

  • print_eco_info, 打印环保信息,如果油耗大于 10,则打印不及格,大于 6 小于等于 10,则打印及格,小于等于 6 则打印优秀


答案:

class Car:def __init__(self, miles, fuel, horsepower):self.total_miles = milesself.fuel = fuelself.horsepower = horsepowerpassdef introduce(self):print("我的里程:", self.total_miles)print("我的油耗:", self.fuel)print("我的马力:", self.horsepower)def print_eco_info(self):if self.fuel > 10 :print("不及格")elif self.fuel > 6:print("及格")else:print("良好")
car1 = Car(14000, 11, 200)
car2 = Car(2000,5, 175)
car1.introduce()
car1.print_eco_info()
print("")
car2.introduce()
car2.print_eco_info()

输出

我的里程: 14000
我的油耗: 11
我的马力: 200
不及格
我的里程: 2000
我的油耗: 5
我的马力: 175
良好

Python数据分析高薪实战第二天 python基础语法复习相关推荐

  1. Python数据分析高薪实战第一天 python基础与项目环境搭建

    开篇词 数据赋能未来,Python 势不可挡 你好,我是千帆. 互联网公司从红利下的爆发期,进入新的精细化发展阶段,亟须深入分析与挖掘业务与数据价值,从而找到新的增长点突破现有增长瓶颈.各行各业的数据 ...

  2. 视频教程-Python数据分析案例实战 视频课程-Python

    Python数据分析案例实战 视频课程 计算机硕士,多年工作经验,技术和产品负责人. 多年推荐系统/NLP/大数据工作经验. 负责公司多个AI项目产品落地,包括文本分类.关键词抽取.命名实体识别.对话 ...

  3. Python数据分析高薪实战第四天 构建国产电视剧评分数据集

    10 实战:手把手教你构建国产电视剧评分数据集 在前面几讲,我们已经学习完了爬虫技术的三个基础环节:下载数据.提取数据以及保存数据. 今天我们将通过一个综合的实战案例来将之前的内容都串联起来,帮你加深 ...

  4. Python数据分析高薪实战第十二天 网络服务用户流失预测分析和国产电视剧评分预测分析

    29 综合实战:网络服务用户流失预测与分析 绝大多数互联网公司都面临一个非常重要的问题:用户流失问题.随着互联网和移动互联网的充分发展,发展新用户(也就是一般所说的拉新)的成本越来越高,往往要几块或者 ...

  5. Python数据分析高薪实战第十天 数据绘图进阶与实战

    24 图像的脊柱.注解和图例:如何画出更专业的图表? 前面两节课,我们学习了主流的几种 matplotlib 的图形形式,主要包括折线图.散点图.直方图.条形图和饼图.现在我们已经可以画出样式比较多的 ...

  6. Python数据分析高薪实战第十天 EDA实战-全球新冠肺炎确诊病例趋势分析

    27 初识 EDA:全球新冠肺炎确诊病例趋势分析 从本讲开始,我们会通过四个具体的案例来将我们之前学习的 Python 数据分析方面的知识全都串起来.一方面能够融会贯通,另一方面也能帮你掌握数据分析基 ...

  7. Python数据分析高薪实战第四天 python数据采集下载和提取保存

    06 获取数据:公开数据集与 DIY 数据集 首先恭喜你完成了第一部分的学习.现在你已经基本掌握了 Python 的基础并能够使用 Python 完成一些相对完整的功能的开发,是时候开始进入数据分析的 ...

  8. Python数据分析高薪实战第八天 数据计算统计与分析

    17 如何快速实现数据的批量计算? 接下来我们会进入一个全新的模块:数值类数据分析.在这个部分,我们会学习数据分析中常用的数学方法以及 Python 中处理数值数据的神器:NumPy. 在完成了本部分 ...

  9. Python数据分析高薪实战第七天 数据解析和电商行为分析案例

    15 时间序列:时间数据的解析与应用 在很多数据分析任务中,经常会遇到处理时间相关的数据.比如电商网站经常需要根据下单记录来分析不同时间段的商品偏好,以此来决定网站不同时间段的促销信息:又或者是通过对 ...

  10. Python数据分析高薪实战第六天 数据过滤与数据清洗

    13 高级索引:过滤与查看表格中的局部数据 上一节课中,我们学习了 pandas 中两个核心的数据结构:Series 和 DataFrame,之后还学习了 DataFrame 的常见操作,比如对列.行 ...

最新文章

  1. java开发常见的热词奇解
  2. docker 2375 vulnerability and self-signatuer certifications
  3. 吴军信息论40讲_吴军信息论40讲笔记
  4. java文件复制速度_java中文件复制得速度测试
  5. 鸿蒙2.0手机版体验,华为鸿蒙OS 2.0手机版功能抢先曝光:体验前所未有
  6. 无线发射机中的正交上变频技术--USRP X410软件无线电平台开发
  7. lammps软件介绍与使用
  8. Linux下安装PyQt4
  9. 基于python的税额计算器
  10. win10系统重装步骤详解
  11. 和领导相处的10条法则,越早知道越好
  12. word 插入表格,位置不在最左边
  13. USR-TCP232-E2串口转以太网模块的使用方法
  14. Canvas如何等待所有图片加载完成才开始绘图
  15. 数据库建模工具Aquarius Orm Studio发布正式版本
  16. silent-interface(OSPF)接口静默
  17. 木木机器人tf卡_Microsoft 微软 L5V-00022 Sculpt Ergonomic 人体工程学无线键鼠套装
  18. HCIA:动态路由路由协议RIP及DHCP
  19. Stephen 博客开通
  20. 无效的源发行版,解决方案

热门文章

  1. 记录一次烧写Attiny85引导加载程序(Bootloader)的过程(制作badusb)
  2. 为什么计算机和网络设备都需要接地,一个实例全面讲解机房如何做防雷接地?...
  3. 5G通信网络专题报告:新一轮移动通信网络建设迎来高潮
  4. 消费者生产者问题,哲学家问题
  5. android模拟拍照,Android调用系统相机拍照,并且模仿实现水印相机简单功能
  6. c51单片机音乐门铃C语言程序,51单片机音乐门铃的设计
  7. 视频源与广播电视制式
  8. 东南亚跨境电商shopee平台,教你轻松打造高销量品牌店铺!
  9. epoll + 非阻塞IO + openssl
  10. css 识别屏幕大小自适应