快乐学Python,Python基础之代码复用?「函数」

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

上一篇文章中有个案例:根据考试分数打印安全意识分级(优秀、及格和不及格)的代码,如下所示:

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

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

a = 75b = 90c = 66if 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 中相似结构的代码复用通过函数来实现。了解了函数,我们才能真正意义上编写复杂的代码。

1、什么是函数

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

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

def 函数名():代码块

其中的关键要素是:

  • def, Python 的关键字,代表我们接下来要创建一个函数;
  • 函数名,顾名思义,函数的名字,名字需要符合变量起名的定义;
  • 冒号,大家应该很熟悉了,这代表冒号后面的代码块的名字是冒号前面的【函数名】;
  • 代码块,也就是我们想起名字来指代的代码块,这种函数对应的代码块,我们也叫函数体。注意这里的代码块和 if 语句的代码块一样,需要缩进。按照 Python 的规则,在冒号后面的代码块都需要相对于冒号所在的行缩进。

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

函数名()

(1)小实战:打印字符三角形

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

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

输出:

  AAAAAAAAA

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

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

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

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

print_triangle()

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

  AAAAAAAAA

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

print_triangle()print_triangle()print_triangle()

输出是这样的:

  AAAAAAAAAAAAAAAAAAAAAAAAAAA

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

2、函数的参数

(1)有“变化”的变量

现在我们回过头去看开篇遇到的给三个同学的分数分级的问题。我们之前通过三个 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, ...):代码块

老规矩,我们举一个非常简单的例子加深理解。假设我们编写一个函数:打印某个数 +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。

(2)使用函数参数改造分级函数

我们还是通过例子来理解,我们希望 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 = 75b = 90c = 66print_level(a)print_level(b)print_level(c)

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

及格优秀及格

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

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

(3)小实战:打印学生信息

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

姓名:小明年龄:12班级:九年一班

通过分析可以发现,打印前缀的提示,比如“姓名”“年龄”这些是不变的,而具体每个学生的姓名、年龄以及班级信息都是会随着学生而变化的。那显而易见,这里我们需要三个参数。

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

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

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

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

print_info("小明",11, "九年一班")print_info("小红",12, "九年二班”)

输出如下:

姓名:小明年龄:11编制:九年一班姓名:小红年龄:12编制:九年二班

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

3、函数的返回值

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

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

问题分析:计算三个同学分数的平均值,本质上就是计算三个数字的平均值。那代表这个函数需要接受 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 什么?怎么拿到函数内部计算的均值?

(1)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 resultfinal_score = three_average(a, b, c)print(final_score)

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

  • 通过 return 语句,把 result 变量返回给外部;
  • 外部调用的时候,我们把函数返回的结果赋值给变量 final_score;
  • 打印出 final_score 的值。

输出结果为 77。

77.0

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

(2)小实战:编写获取学生分级的函数

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

  • 明确需求:就是函数具体要做一个什么事情,这就要求我们理解任务/需求的描述。
  • 明确输入:就是想清楚函数有哪些参数,扩展来说就是函数要完成上面的任务,哪些变量是需要从外部传入的。
  • 明确输出:就是函数需不需要返回值,以及返回值是什么,这同样取决于第一步,需求是什么。比如我们只是想让函数内部打印一些内容,那一般不需要返回值。而如果我们希望函数能够做一些计算,并将结果告诉调用方时,则需要返回值。

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

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

  • 明确输入:学生的分数。

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

根据上面的分析,以及 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 三个变量上。

4、Python 的库函数

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

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

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

  • 打印信息倒屏幕的 print 语句;
  • 计算字符串和列表长度的 len 语句。

本质都是 Python 的库函数。

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

  • len 函数:接收一个参数,类型是列表或者字符串,返回值为列表和字符串的长度。
  • print 函数:接收一个或多个字符串,把他们打印在屏幕上,没有返回值。

Python 的库函数非常多,感兴趣的可以到 Python 的官方文档库浏览学习。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/601931.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

盖子的c++小课堂——第二十三讲:背包问题

前言 又是一次漫长的更新&#xff08;我真不是故意的aaaaaaaaaaaaaaa&#xff09;&#xff0c;先不多说了&#xff0c;直接给我~坐下~说错了说错了&#xff0c;直接开始~ 背包问题----动态规划 背包问题&#xff08;knapsack problem&#xff09; 动态规划&#xff08;dyna…

基于python的leetcode算法介绍之动态规划

文章目录 零 算法介绍一 例题介绍 使用最小花费爬楼梯问题分析 Leetcode例题与思路[118. 杨辉三角](https://leetcode.cn/problems/pascals-triangle/)解题思路题解 [53. 最大子数组和](https://leetcode.cn/problems/maximum-subarray/)解题思路题解 [96. 不同的二叉搜索树](h…

企业出海数据合规:GDPR中的个人数据与非个人数据之区分

GDPR仅适用于个人数据&#xff0c;这意味着非个人数据不在其适用范围内。因此&#xff0c;个人数据的定义是一个至关重要的因素&#xff0c;因为它决定了处理数据的实体是否要遵守该法规对数据控制者规定的各种义务。尽管如此&#xff0c;什么是个人数据仍然是当前数据保护制度…

uniapp微信小程序投票系统实战 (SpringBoot2+vue3.2+element plus ) -小程序首页实现

锋哥原创的uniapp微信小程序投票系统实战&#xff1a; uniapp微信小程序投票系统实战课程 (SpringBoot2vue3.2element plus ) ( 火爆连载更新中... )_哔哩哔哩_bilibiliuniapp微信小程序投票系统实战课程 (SpringBoot2vue3.2element plus ) ( 火爆连载更新中... )共计21条视频…

Docker 镜像以及镜像分层

Docker 镜像以及镜像分层 1 什么是镜像2 Docker镜像加载原理2.1 UnionFs&#xff1a;联合文件系统2.2 Docker镜像加载原理2.3 Docker镜像的特点 3 镜像的分层结构4 可写的容器层 1 什么是镜像 镜像是一种轻量级、可执行的独立软件包&#xff0c;用来打包软件运行环境和基于运行…

数据处理四 基于图像hash进行数据整理(删除重复图片、基于模版查找图片)

一、背景知识 1.1 什么是hash Hash&#xff0c;一般翻译做“散列”&#xff0c;也有直接音译为“哈希”的&#xff0c;基本原理就是把任意长度的输入&#xff0c;通过Hash算法变成固定长度的输出。这个映射的规则就是对应的Hash算法&#xff0c;而原始数据映射后的二进制串就…

程序员必知!命令模式的实战应用与案例分析

命令模式是一种行为设计模式&#xff0c;它将请求封装为对象以实现客户端参数化、请求排队、日志记录及撤销操作&#xff0c;旨在解耦调用者与操作实现者&#xff0c;以智能家居为例&#xff0c;用户通过界面发送命令对象&#xff0c;设备作为接收者执行相应操作&#xff0c;无…

Ubuntu 安装Nginx服务

文章目录 前言一、Nginx安装1. Nginx默认安装2. Nginx指定版本安装3. Nginx验证4. Nginx服务控制4.1 查看服务状态4.2 停止服务4.3 启动服务4.4 重启服务 5. Nginx文件存放目录 二、自己编译Nginx1. 下载源码2. 依赖配置3. 编译 三、Nginx卸载总结 前言 Nginx&#xff08;发音为…

SDN和NFV最明显的区别,SDN和传统网络有什么区别

目录 SDN和NFV最明显的区别是 SDN和传统网络有什么区别 一、SDN概述 1.1 S

Fixed win size sliding window

这篇记录灵神题单中的定长滑窗环节&#xff0c;不跟之前的Sliding Window一起了。 1. LC 1423 可获得的最大点数 这题其实有点思维的。实现上简单。 每次从首或者尾部拿&#xff0c;总共拿k次。相当于有n-k张牌不拿。因为不可能从中间截断着拿&#xff0c;因此必然这n-k张牌…

分布式(2)

目录 6.Ribbon和Feign的区别有哪些&#xff1f; 7.SpringCloud和dubbo有什么区别&#xff1f; 8.服务注册和发现是什么意思&#xff1f;SpringCloud如何实现&#xff1f; 9.介绍下网关的作用&#xff1f; 10.谈谈服务降级&#xff0c;熔断&#xff0c;服务隔离&#xff1f;…

机器学习(四) -- 模型评估(3)

系列文章目录 机器学习&#xff08;一&#xff09; -- 概述 机器学习&#xff08;二&#xff09; -- 数据预处理&#xff08;1-3&#xff09; 机器学习&#xff08;三&#xff09; -- 特征工程&#xff08;1-2&#xff09; 机器学习&#xff08;四&#xff09; -- 模型评估…

通货紧缩,通货膨胀

目录 通货紧缩 通货膨胀 通货紧缩 是一个与通货膨胀相对应的经济学概念&#xff0c;是由货币供给量相对于经济增长和劳动生产率提高等要素减少而引致的有效需求严重不足 一般物价水平持续下跌&#xff0c;货币供给量持续下降和经济衰退的现象。当市场上流通的货币减少&…

探索 OceanBase 中图数据的实现

在数据管理和处理的现代环境中&#xff0c;对能够处理复杂数据结构的复杂数据模型和方法的需求从未如此迫切。图数据的出现以其自然直观地表示复杂关系的独特能力&#xff0c;开辟了数据分析的新领域。 虽然 Neo4j 等成熟的图形数据库为处理图形数据提供了强大的解决方案&…

Python从入门到网络爬虫(模块详解)

模块 我们知道&#xff0c;函数和类都是可以重复调用的代码块。在程序中使用位于不同文件的代码块的方法是&#xff1a;导入 (import) 该对象所在的模块 (mudule)。当程序变得越来越大时&#xff0c;将程序的不同部分根据不同分类方法保存在不同文件中通常会更加方便。 导入模…

python抓取异步数据(加载更多类型的),并下载图片,保存到文件夹,实例

源码: {"subjects":[{"episodes_info":"","rate":"8.8","cover_x":3000,"title":"奥本海默","url":"https:\/\/movie.douban.com\/subject\/35593344\/","playabl…

hydra爆破

导语 Hydra 是一个用于暴力破解登录凭据的网络安全工具。它可以通过尝试多个用户名和密码组合来攻击目标系统,以获得未经授权的访问权限。 Hydra 支持多种协议和服务,包括常见的 SSH、FTP、Telnet、HTTP、SMTP 等。它可以通过使用字典文件或生成密码的模式进行强制破解。可以…

如何理解Transformer论文中的positional encoding,和三角函数有什么关系?

大家好&#xff0c;我分享交流下这个问题。 Positional Encoding 掏出一张被无数人讲述的架构图。 Transformer 模型中的位置编码&#xff08;Positional Encoding&#xff09;是为了让模型能够考虑单词在句子中的位置。 由于 Transformer 的自注意力&#xff08;Self-Atte…

bat批处理文件_bat注释汇总

文章目录 1、示例&#xff08;直接结合脚本和结果进行理解&#xff09; 1、示例&#xff08;直接结合脚本和结果进行理解&#xff09; %这是一个注释% %这是另一个注释%rem 这是一个注释 rem 这是另一个注释:这是一个注释 ::这是一个注释 :?这是另一个注释if 1 1 ( %这里会执…

Golang 切片

前言 在Go语言中&#xff0c;切片是一个引用类型&#xff0c;它提供了对数组的动态窗口。切片并不存储任何数据&#xff0c;它只是描述了底层数组中的一个片段。切片的定义包括三个部分&#xff1a;指向数组的指针、切片的长度和切片的容量 基本使用 声明切片&#xff1a;声…