一、前言
紧跟学校课程安排,目前我们正在学习有关Python函数的相关知识,所以我就来对Python函数的使用进行一个简单的总结,后续老师会布置对应的实验,我也会第一时间的更新实验内容的。
函数这一章内容比较重要,后续会经常用到,也是常考点,需要大家认真学习并练习。
二、我的环境
- 电脑系统:Windows 11
- 语言版本:Python 3.10.4
- 编译器:VSCode
三、函数的定义与调用
简单来说函数是一段具名的、实现特定功能的、可重用的代码段,函数提供了一种进行巩固抽象和代码重用的手段,在Python中函数定义一般形式如下:
def函数名(形参列表): """函数文档字符串"""函数体 [return [返回值]]
例如,我们定义一个简单函数来进行说明一下:
deffact(n): """计算n的阶乘。"""k=1foriinrange(2, n+1): k*=ireturnkprint(fact(5)) print(fact.__doc__)
它运行的结果是:
120
计算n的阶乘。
这里,def关键字是Python中定义函数的开始,在一个项目中国函数名必须唯一,如果出现同名就会被覆盖但不会被报错,圆括号及其后的冒号必须要有,内部的形式参数可以有多个但要用逗号隔开,也可以不填入参数。
函数如果有参数或者返回值的话,它们的数据类型都不用声明,因为Python是一种动态语言,可自动识别对象类型。
函数文档字符串需要使用一对三个单引号或者三个双引号包围,允许对函数巩固、参数等进行说明,可以省略但最好写养成良好习惯,我们可以调用__doc__方法查看。
最后我们可以使用return语句返回一个或多个值,也可以只有return关键字而没有返回值,还可以没有return语句,函数在执行return语句之后就会直接退出函数执行,所以在return语句之后的语句将不会被执行。
四、传递实参
Python参数传递的方式是传递对象的引用(即对象的内存地址),而不是传递对象的值。
1、位置实参
调用函数时,Python必须将函数调用中的每个实参都关联到函数定义中的一个形参,最简单的关联方式是基于实参的顺序,这种关联方式称为位置实参。
举个例子:
defdescribe_pet(animal_type, pet_name): """显示宠物信息。"""print("我有一个{}。".format(animal_type)) print("我的{}名字是{}。".format(animal_type, pet_name.title())) describe_pet("仓鼠", "杰瑞")
它运行的结果是:
我有一个仓鼠。我的仓鼠名字是杰瑞。
需要注意的是位置实参的顺序很重要,如果顺序错误,输出的结果就会不对应。
2、关键字实参
关键字实参是传递给函数的名称值对,因为直接在实参中将名称和值关联起来,所以向函数传递实参时不会混淆,关键字实参让你无须考虑函数调用中的实参顺序,还清楚地指出了函数调用中各个值的用途。
还是上面那个例子我们稍微改一下:
defdescribe_pet(animal_type, pet_name): """显示宠物信息。"""print("我有一个{}。".format(animal_type)) print("我的{}名字是{}。".format(animal_type, pet_name.title())) describe_pet(animal_type="仓鼠", pet_name="杰瑞")
我们指定实参值之后就不会出现顺序错误导致输出错误了。
3、默认值
我们在编写函数的时候可以给形参赋默认值,如果我们没有为它赋予实参则它将默认使用默认值。
我们继续将上面那个例子进行修改:
defdescribe_pet(animal_type="仓鼠", pet_name): """显示宠物信息。"""print("我有一个{}。".format(animal_type)) print("我的{}名字是{}。".format(animal_type, pet_name.title())) describe_pet(pet_name="杰瑞")
这里我们最后只传入了一个实参,但输出结果跟上面的一样就是因为我们设置了默认值。
4、可变长度参数
可变长度参数是指一个形参能够接收的实参数是可变的,它有两种形式:
- 形参前加一个*号,表示可以接收多个位置参数并把他们放到一个元组中
defdemo1(*args): print(args) demo1(1, 2, 3, 4, 5, 6) # 结果为(1, 2, 3, 4, 5, 6)
- 形参前加两个**号,表示可以接收多个关键字参数并把它们放到一个字典中
defdemo2(**kwargs): print(kwargs) demo2(x=1, y=2, z=3) # 结果为{'x': 1, 'y': 2, 'z': 3}
5、避免实参错误
我们需要注意的是我们定义了多少实参最后就需要传入多少个,如果传入的实参与函数的不匹配就会出现报错,这一点我们需要注意。
五、返回值
函数并非总是直接显示输出,它还可以处理一些数据,并返回一个或一组值,函数返回的值称为返回值,它可以将程序的大部分繁琐的工作放到函数内去完成,从而简化主程序。
1、返回简单值
例如返回姓名:
defget_formatted_name(first_name, last_name): """返回姓名"""full_name=f"{first_name}{last_name}"returnfull_name.title() musician=get_formatted_name("jimi", "hendrix") print(musician)
它运行的结果是:
JimiHendrix
2、让实参变成可选的
有些时候我们需要将实参变成可选的,这里我们就会用到前面所讲的默认值。
例如,假如我们要给前面定义的函数添加处理中间名的功能:
defget_formatted_name(first_name, middle_name, last_name): """返回姓名"""full_name=f"{first_name}{middle_name}{last_name}"returnfull_name.title() musician=get_formatted_name("john", "lee", "hooker") print(musician)
它运行的结果是:
JohnLeeHooker
但是并不是所有人都有中间名,所以现在我们需要做的就是将中间名这个参数变成可选的,那就是给它赋予默认值,且默认值为空字符串,如果传入该参数就使用实参,没有传入就使用默认值空字符串代替:
defget_formatted_name(first_name, last_name, middle_name=''): """返回姓名"""ifmiddle_name: full_name=f"{first_name}{middle_name}{last_name}"else: full_name=f"{first_name}{last_name}"returnfull_name.title() musician=get_formatted_name("jimi", "hedrix") print(musician) musician=get_formatted_name("john", "lee", "hooker") print(musician)
它运行的结果是:
JimiHedrixJohnHookerLee
3、返回字典
函数可返回任何类型的值,包括列表和字典等较复杂的数据结构,举个例子:
defbuild_person(first_name, last_name, age=None): """返回一个字典,其中包含有关一个人的信息。"""person= {'姓': first_name, '名': last_name} ifage: person['年龄'] =agereturnpersonmusician=build_person('北', '天',age=21) print(musician)
它运行的结果是:
{'姓': '北', '名': '天', '年龄': 21}
六、传递列表
我们后面肯定会经常用到传入列表然后使用循环打印出列表的每一个值,例如我们定义一个用户列表然后使用函数对其中每个人打个招呼:
defgreet_users(names): """向列表中的每个人打招呼。"""fornameinnames: msg=f"Hello {name}, How are you?"print(msg) usernames= ['娜娜', '北天'] greet_users(usernames)
它运行的结果是:
Hello娜娜, Howareyou?Hello北天, Howareyou?
七、变量作用域
变量作用域是指能够访问该变量的代码范围,分为三个层次,它们分别是局部作用域、外围作用域和全局作用域,其中:
- 全局作用域:作用到整个模块中
- 外围作用域:可以作用到内嵌函数
- 局部作用域,仅作用于本函数内部
另外,不同作用域内的变量名可以相同,如果同名变量作用域出现重叠,除非特别声明,否则作用域小的变量将屏蔽作用域大的同名变量。
举个例子:
x=3defoutfunc(): x=5definnerfunc(): x=7print('inner x:', x) innerfunc() print('outer x:', x) outfunc() print('global x:', x)
它运行的结果是:
innerx: 7outerx: 5globalx: 3
需要注意的是,如果在内部函数中不修改非局部变量(包括全局变量和外围非全局变量)的值,而只是读取其值,则不用专门声明。
例如:
x=3defoutfunc(): y=5definnerfunc(): z=7print('inner sum:', x+y+z) innerfunc() print('outer sum:', x+y) outfunc()
它运行的结果是:
innersum: 15outersum: 8
还有就是,如果在内部函数中需要修改非局部变量的值,则需要专门声明,其中修改全局变量需要使用关键字global声明,修改外围非全局变量需要使用关键字nonlocal声明。
例如:
x=3defoutfunc(): globalxx+=2y=5definnerfunc(): globalxnonlocalyx*=2y*=2z=7print('inner x, y, z:', x, y, z) print('inner sum:', x+y+z) innerfunc() print('outer x, y:', x, y) print('outer sum:', x+y) outfunc() print('global x:', x)
它运行的结果是:
innerx, y, z: 10107innersum: 27outerx, y: 1010outersum: 20globalx: 10
八、匿名函数
所谓匿名函数就是没有函数名的函数,常用于需要快速定义一个单行、功能简单的函数场合,Python通过关键字lambda来定义匿名函数,它是一个表达式,所以也叫lambda表达式。
它的一般语法格式是:
lambda [参数1 [, 参数2, ...参数n]]: 表达式
其参数列表允许为空,也不用写圆括号,表达式中不允许包含其他复杂的语句,但允许调用其他函数,也不用return返回值,表达式的结果就是表达值。
匿名函数可以赋值给变量(此时变量就引用了该匿名函数对象),并用该变量后加圆括号来调用该匿名函数,也可以不赋值给变量。
例如:
# 通过赋值给变量来调用lambda表达式t=lambda: print("进行匿名函数演示") t() f=lambdax, y: x+yprint(f(2, 3)) # 下面例子没有把lambda表达式赋值给变量defsum1(a, b, c): returnlambda: a+b+cprint(sum1(1, 2, 3)())
它运行的结果是:
进行匿名函数演示56
其中需要注意的是,最后一行sum1(1, 2, 3)的后面如果没有(),则输出的是返回的函数对象的内存地址。
九、最后我想说
函数部分的内容还远远没有总结完,还有超级多的知识需要大家去学习,上述文章只是总结了其中一部分知识,函数还有很多高阶函数的使用,后面如果用到了我再进行总结,目前就总结这么多吧,如果后续还有补充的我也会进行更新的。
网上有很多有关Python知识总结的博客和书籍,大家可以多去看看书,多去看看大佬总结的博客,我们一起学习。
欢迎大家进行留言讨论问题,也期待得到大家的支持,谢谢!