第 5 章 函数入门概念及分类函数的概念函数function是**组织好的、可重复使用的、用于执行特定任务**的代码块。:::info举个生活中的例子函数就像是智能家居中的一个场景我们提前配置好场景中要执行的操作等需要时直接呼唤场景的名字场景中的操作就会开始执行。Python 中的函数是一段有名字的代码块我们提前编写好函数中要执行的代码等需要时调用函数函数中的代码就会执行。:::使用函数的主要优势Python 中函数的分类Python 中函数分为三类①内置函数、②模块提供的函数、③自定义函数。相关官方文档内置函数https://docs.python.org/zh-cn/3.13/library/functions.html模块提供的函数https://docs.python.org/zh-cn/3.13/py-modindex.html:::tips****备注内置函数与模块提供的函数 Python 都已经提前定义完毕我们只管调用即可。本章主要讲解自定义函数对于内置函数和模块提供的函数后面用到哪个讲哪个。:::基本使用定义函数1️⃣****语法格式如下语法格式是函数的基本定义不涉及接收参数和返回值。def函数名():函数体 函数体2️⃣****语法图解3️⃣说明:::info定义函数的关键字是def需要将def与函数名用空格隔开随后紧跟():。函数的命名遵循我们之前讲过的『标识符命名规范』。函数定义完毕后只是告诉 Python 我们定义了一个函数可以完成某些功能但此时函数体还没有执行需要调用函数后函数体才会执行:::4️⃣**示例代码**定义一个名为welcome的函数函数体中打印两句欢迎语。# 定义函数defwelcome():print(欢迎来到尚硅谷课堂)print(尚硅谷让天下没有难学的技术)调用函数1️⃣****语法格式如下语法格式是基本调用形式不涉及传递参数。函数名()2️⃣****语法图解3️⃣****示例代码编写代码调用我们刚才定义的welcome函数。# 定义函数defwelcome():print(欢迎来到尚硅谷课堂)print(尚硅谷让天下没有难学的技术)# 调用函数让函数中的代码运行起来welcome()welcome()welcome():::color4**⚠️****注意**函数必须先定义再调用。:::参数参数的作用参数可以让函数接收外部传入的数据能让函数更具通用性和灵活性比如下面的这个需求:::info******需求**定义一个名为order的函数在函数体中打印用户的点餐信息。:::用如下代码实现就会面临两个问题**① **每次的点餐数量只能是一份。②每次点的菜品只能是辣椒炒肉。# 定义函数deforder():num1dish辣椒炒肉print(f您点的是{num}份{dish})# 调用函数order()但如果在上述代码的基础上使用参数就可以灵活修改点餐数量和菜品写法如下# 定义函数定义的同时声明需要两个参数分别是菜品数量 num和菜品名称 dishdeforder(num,dish):print(f您点的是{num}份{dish})print(f{dish}可是很好吃的)print(f你只点了{num}份够吃吗\n)# 调用函数调用的同时传递了两个值order(1,辣椒炒肉)order(2,辣子鸡)实参与形参在使用函数时要注意区分『形参』与『实参』。形参形式参数在定义函数时用来接收数据的变量叫形参形参是函数定义者设置的。实参实际参数在调用函数时给函数传递的具体值叫实参实参是函数调用者提供的。:::tips****备注形参存储的到底是什么数据要看调用者传递的实参具体是什么。::::::color4**注意**形参的使用范围仅限函数体内。:::位置参数**位置参数**调用函数时根据参数在函数定义时出现的顺序把实参的值依次传递给对应的形参。:::info例如在上一小节所写的order函数就是在使用位置参数其中形参与实参的对应关系如下图::::::color4**注意**在使用『位置参数』时实参的个数与顺序必须和形参保持一致:::deforder(num,dish):print(f您点的是{num}份{dish})print(f{dish}可是很好吃的)print(f你只点了{num}份够吃吗\n)# 以下是错误示范order(3)# 参数少了order(4,宫保鸡丁,7)# 参数多了order(宫保鸡丁,4)# 实参顺序没有和形参保持一致不会报错但会造成数据错乱。关键字参数**关键字参数**函数调用时通过**形参名 值**的形式传递的参数就是关键字参数。关键字参数的优势是不受顺序限制。# 定义函数defgreet(name,gender,age,height):print(f我叫{name}性别{gender}年龄是{age}身高是{height}cm)# 调用函数使用关键字参数greet(name张三,gender男,age18,height172)greet(height172,age18,gender男,name张三):::color4注意『位置参数』和『关键字参数』可以混用但『位置参数』必须写在『关键字参数』之前:::# 正确使用方式greet(张三,男,height172,age18)# 错误示例greet(height172,age18,张三,男)greet(name张三,男,18,172)greet(name张三,男,age18,172)greet(height172,age18,gender男,name张三,age19)greet(height172,age18,gender男,name张三,school尚硅谷)限制传参方式具体限制方式/前面只能用『位置参数』*后面只能用『关键字参数』。# 定义函数使用/和*限制传参方式defgreet(name,/,gender,*,age,height):print(f我叫{name}性别{gender}年龄是{age}身高是{height}cm)# 正确示例greet(张三,男,age18,height172)greet(张三,gender男,age18,height172)# 错误示例greet(name张三,gender男,age18,height172)greet(张三,男,18,height172)参数默认值在定义函数时可以通过**font stylecolor:rgb(38, 38, 38);形参名 值/font**的形式为形参设置一个默认值这样就可以实现若调用函数时**没有传入**该参数的值就使用默认值。若调用函数时**传入了**该参数的值就使用传入的值。# 定义函数设置参数默认值defgreet(name,gender,age,height,msg你好):print(f我叫{name}性别{gender}年龄是{age}身高是{height}cm)print(f我想说{msg})# 调用函数greet(张三,男,18,172)greet(张三,男,18,172,hello)greet(张三,男,18,172,msghello):::color4**注意**定义函数时『默认参数』必须放在『必选参数』的后面或者换一种说法就是某个形参一旦设置了默认值那它后面的所有形参也必须要写默认值:::例如下面的代码中msg你好这个默认参数居然写在了位置参数height前面所以就会报错。# 定义函数设置参数默认值的错误示例defgreet(name,gender,age,msg你好,height):print(f我叫{name}性别{gender}年龄是{age}身高是{height}cm)print(f我想说{msg})可变参数在定义函数时如果不确定会传入多少个参数那就可以使用可变参数具体写法有两种使用*形参名来接收任意数量的『位置参数』多个位置参数最终会被打包成一个『元组』。使用**形参名来接收任意数量的『关键字参数』多个关键字参数最终会被打包成一个『字典』。:::tips******备注**元组和字典都是新的数据类型后面才会讲但没关系这不耽误大家理解本小节的内容。:::# 定义函数使用*args去接收可变位置参数args只是大家习惯这么写当然也可以换成其他变量deftest1(*args):# 此处args的值是一种新的数据类型叫元组我们下一章就去讲元组print(args)# 调用函数test1(张三,男,18,172)# 定义函数使用**kwargs去接收可变关键字参数kwargs只是大家习惯这么写当然也可以换成其他变量deftest2(**kwargs):# 此处kwargs的值是一种新的数据类型叫字典我们下一章就去讲字典print(kwargs)# 调用函数test2(name张三,gender男,age18,height172)『可变位置参数』和『可变关键字参数』可以同时使用但必须要**先写****『可变位置参数』**。# 定义函数同时使用可变位置参数、可变关键字参数deftest3(a,b,*args,c尚硅谷,**kwargs):print(a)print(b)print(c)print(args)print(kwargs)# 调用函数test3(张三,男,抽烟,喝酒,age18,height172)特殊的字面量 NoneNone 是一个特殊的字面量用来表示空值、无值、无意义。例如msg None的含义是 —— 我先定义一个变量msg但目前还不知道它会存储什么类型的值那能不能写成msg 0呢这要看具体情况:::info如果确定msg之后会存放数值类型的数据那这样写是可以的。但如果还不确定msg将来会存放什么类型的数据最好不要写成msg 0否则可能会误导别人以为它一定是数值类型。:::所以使用 None 更加中立、开放因为它不暗示变量的类型。None 的官方文档https://docs.python.org/zh-cn/3.13/library/constants.html#None:::info****几个关键点None的类型是NoneType。font stylecolor:rgb(38, 38, 38);None/font出现在布尔判断中(font stylecolor:rgb(38, 38, 38);if/font判断条件、font stylecolor:rgb(38, 38, 38);while/font循环条件)会被当作font stylecolor:rgb(38, 38, 38);False/font来处理。None不能参与任何数学运算也不能与字符串拼接。不给函数设置返回值那函数默认就会返回None:::None出现最多的两个场景:::info1️⃣函数中没有写return或写了return但没有返回任何内容 。2️⃣变量定义时暂时还不知道要存放什么可以先赋值为None。:::# None是一个特殊的字面量它表示空值 / 无值 / 无意义。msgNone# None 的类型是 NoneType。print(type(msg))# None 转为布尔值是 False。print(bool(msg))ifnotmsg:print(你好)# 不能参与数学运算也不能与字符串拼接。# result1 msg 1# result1 msg hello返回值什么是返回值**函数返回值**函数执行完毕后会把执行结果交给调用者这个执行结果就是函数的返回值。我们之前用过的这些内置函数都有返回值对于自定义的函数即便我们不去设置返回值函数也会默认返回None由于None表示空所以如果一个函数的返回值是None的话就也可以说这个函数“没有”返回值。# 定义函数defadd(n1,n2):print(f我收到了{n1}、{n2}二者相加是{n1n2})print(add函数执行完毕了)# 调用函数resultadd(100,200)print(result)# None如何设置返回值使用return关键字可以设置函数的返回值return的作用有两个分别是结束函数的运行。把return后面的值作为函数的返回值。# 定义函数defadd(n1,n2):print(f我收到了{n1}、{n2}二者相加是{n1n2})print(add函数执行完毕了)returnn1n2# 调用函数resultadd(100,200)print(result)# print函数是没有返回值的resprint(hello)print(res)全局作用域 VS 局部作用域什么是作用域作用域就是变量能**起作用的范围**变量在哪里能用在哪里不能用Python 中有多种作用域我们先来学习全局作用域、局部作用域。全局作用域 _ 全局变量**全局作用域**整个.py文件最外层的范围就是全局作用域。**全局变量**写在全局作用域中的变量就叫全局变量全局变量在整个程序中都可以访问。局部作用域 _ 局部变量**局部作用域**函数的内部范围就是局部作用域。**局部变量**写在局部作用域中函数内部的变量叫局部变量它只能在当前函数中使用。global 关键字在函数内部使用global关键字可以声明变量为全局变量。a100deftest():globala# 使用 global 关键字将a声明为全局变量。a300print(函数中的打印a,a)test()print(全局的打印a,a)小测试请说出如下代码的输出结果具体分析请参考视频教程# 全局作用域 与 局部作用域以及global的使用a100b200deftest():c尚硅谷d你好啊globala a300print(函数中的打印a,a)print(函数中的打印b,b)print(函数中的打印c,c)print(函数中的打印d,d)test()print(***************)print(全局的打印a,a)print(全局的打印b,b)print(c)print(d)# 局部作用域 和 局部变量会在函数调用时创建在函数执行结束后自动销毁deftest2():m100m1print(f我是test2函数中打印的m{m})test2()test2()test2()# 全局作用域 与 全局变量会在程序开始时创建在程序结束后销毁n100deftest3():globaln n1print(f我是test3函数中打印的n{n})test3()test3()test3()print(n)嵌套调用**嵌套调用**在一个函数执行的过程中调用了另外一个函数例如下面的代码如下代码的具体分析过程请参考视频教程。# 函数嵌套调用测试1defgreet(name,msg):print(f我叫{name}我想说的话在下面)speak(msg)print(嗯我想说的结束了)defspeak(msg):print(----------)print(msg)print(----------)greet(张三,你好啊)# 函数嵌套调用测试2deftest1():print(进入 test1 函数)test2()print(退出 test1 函数)deftest2():print(进入 test2 函数)test3()print(退出 test2 函数)deftest3():print(进入 test3 函数)print(***正在执行 test3 函数)print(退出 test3 函数)test1()递归调用1️⃣递归调用函数自己调用自己的一种操作。defwelcome():print(你好啊)welcome()# welcome 函数内部在调用自己welcome():::color4**警告**上述代码确实是递归调用但会出现死循环:::2️⃣递归必须要具备终止条件不能无限的一直调用总得有停下来的时候。如下代码使用递归调用的方式输出了 10 次“你好啊”# 使用递归打印n次“你好啊”从大到小defwelcome(n):print(f你好啊{n})ifn1:welcome(n-1)# 调用函数welcome(5)# 使用递归打印n次“你好啊”从小到大defwelcome(n):ifn1:welcome(n-1)print(f你好啊{n})# 调用函数welcome(5)3️⃣**递归的应用**使用递归完成一个数的阶乘如下代码的具体分析过程请参考视频教程。# 使用递归求阶乘deffactorial(num):ifnum0:return1else:returnnum*factorial(num-1)# 调用函数求5的阶乘resultfactorial(6)print(result)函数说明文档函数说明文档写在函数里的文字说明用来描述函数的功能、需要哪些参数、返回什么结果它的语法和普通字符串一样用三引号包裹defadd(n1,n2): 计算两个数相加的结果 :param n1:第一个数 :param n2:第二个数 :return:二者相加的结果 returnn1n2 resultadd(1,2)有了函数说明文档之后可以通过鼠标悬浮的方式查看函数的具体信息如下图函数综合案例完成一个健身挑战赛程序功能演示见下图具体实现代码如下如下代码的具体分析过程请参考视频教程。defcalc_total(*nums): 计算总运动量个 :param nums: 每一天的运动量可变参数 :return: 总运动量个 # 备注nums的类型是元组下一章马上就讲了sum是内置函数可以对元组中的数据求和returnsum(nums)defcalc_avg(total,days7): 计算平均值 :param total: 总运动量个 :param days: 天数默认值是7 :return: 平均值 returntotal/daysdefcheck_success(total,goal120): 判断本次挑战是否成功 :param total: 总运动量 :param goal: 成功数量默认值为120 :return: 成功或失败的具体信息 iftotalgoal:return✅恭喜挑战成功else:return❌抱歉挑战失败defmain(title,duration,goal): 主函数用于开始一场挑战赛 :param title: 比赛标题 :param duration: 比赛持续天数 :param goal: 目标运动量 :return: None print(f【{title}】【{duration}天】✊️挑战赛请输入每天的数量)num1int(input(第1天))num2int(input(第2天))num3int(input(第3天))# 计算总数totalcalc_total(num1,num2,num3)# 计算平均值avgcalc_avg(total,duration)# 判断挑战是否成功resultcheck_success(total,goal)# 打印相关信息print(f【{title}】【{duration}天】健身总结)print(f总数{total}平均值{avg:.1f})print(result)main(俯卧撑,3,40)