四、函数
日常生活中,要完成一件复杂的功能,我们总是习惯把“大功能”分解为多个“小功能”以实现。在编程的世界里,“功能”可称呼为“函数”,因此“函数”其实就是一段实现了某种功能的代码,并且可以供其它代码调用。
假设我们在编程过程中需要计算圆形的面积。如果我们不是用函数,每次需要计算原型面积的时候都需要这样
r1 = 12.34
r2 = 9.08
r3 = 73.1
s1 = 3.14 * r1 * r1
s2 = 3.14 * r2 * r2
s3 = 3.14 * r3 * r3
这样如果计算次数比较少还可以,如果需要计算的次数非常多,就比较麻烦了。另外如果我们要改动一下公式,比如π的值我们要改成3.141592657,有多少计算的地方就需要改多少遍,麻烦不说,还容易出错,遗漏等。函数就可以有效的解决以上的问题。
1、函数的定义及调用
定义一个函数要使用def关键字,依次写出函数名、括号、括号中的参数和冒号:,然后用缩进的代码块写函数体,函数体内可以调用return语句返回结果,以求原型面积为例我们可以这么定义一个函数
def circle_area(r):
return 3.14 * r *r
这样如果调用这个函数的时候我们只要这样,就可以了。
circle_area(3)
这样如果我们要改动π的值只需要改动一下函数就可以了。
另外函数名作为函数的名称,也可以像变量一样进行赋值操作、甚至作为参数进行传递,例如我们也可以把求圆形面积的函数赋值给f,然后通过f(4)这样去调用函数,意思是一样的。
f = circle_area
f(4)
2、函数的参数
1)位置参数
这是最常见的定义方式,一个函数可以定义任意个参数,每个参数间用逗号分割,例如:
def Foo1(arg1, arg2):
print(arg1, arg2)
用这种方式定义的函数在调用的的时候也必须在函数名后的小括号里提供个数相等的值(实际参数),而且顺序必须相同,也就是说在这种调用中,形参和实参的个数必须一致,而且必须一一对应,也就是说第一个形参对应这第一个实参。例如:
Foo1('abc', 123)
输出结果
abc 123
如果形参和实参的数量不一致就会出现类似如下错误
Traceback (most recent call last):
File "D:/OldBoy_Python_git/OldBoy_Python/day3/def_demo_4.py", line 62, in <module>
foo3('abc')
TypeError: foo3() missing 1 required positional argument: 'arg2'
也可以通过如下方式传递参数,而不必考虑顺序问题,但数量无论如何必须一致。
foo3(arg2 = 123, arg1 = 'abc')
2)默认参数
我们可以给某个参数指定一个默认值,当调用得时候,如果没有指定那个参数,那个参数的值就等于默认值
def Foo2(arg1, arg2 = 123):
print(arg1, arg2)
调用
Foo2('abc')
Foo2('abc', 345)
执行结果
abc 123
abc 345
注意:定义的时候默认参数必须放到所有位置参数的后面进行定义,否则会报语法错误
def Foo2(arg1 = 123, arg2):
^
SyntaxError: non-default argument follows default argument
3)可变参数
顾名思义,可变参数就是传入的参数个数是可变的,可以是1个、2个、甚至于0个
def Foo3(*args):
print(args)
调用
Foo3(1, 2, 'abc')
执行结果
(1, 2, 'abc')
说明:可以看到我们传递了三个参数都被Python转化为元祖,保存到args中了,这样我们就可以通过索引对参数记性调用,或者通过for in进行遍历
4)关键字参数
可变参数在调用过程中会组装成元祖,元祖只能通过索引进行调用,有时候不是很方便,所以Python可以通过关键字索引将传入的参数组装成字典。
def Foo4(**kwargs):
print(kwargs, type(kwargs)) Foo4(k1 = 'abc', k2 = 123)
执行结果
{'k2': 123, 'k1': 'abc'} <class 'dict'>
关键字参数允许传入0个或任意个参数名的参数,0个的话就是一个空字典
3、参数组合
在Python中定义函数,可以用必选参数(位置参数)、默认参数、可变参数、关键字参数这几种参数进行组合使用,但是顺序必须是,必选参数、默认参数、可变参数、关键字参数。
def Foo5(arg1, arg2 = 'abc', *args, **kwargs):
print('arg1:', arg1)
print('arg2:', arg2)
print('args', args)
print('kwargs', kwargs) Foo5(123, 'bcd', 123, 'abc', k1 = 123, k2 = 'abc')
执行结果
arg1: 123
arg2: bcd
args (123, 'abc')
kwargs {'k1': 123, 'k2': 'abc'}
4、lambda匿名函数
在Python中,提供了对匿名函数的支持,所谓匿名函数就是功能非常简单只需要一行代码就可以实现的,比如之前我们我们之前说的求圆形面积的函数,用匿名函数就可以这样定义。
f = lambda r: 3.14 * r * r
print(f(4)) # 调用
执行结果
50.24
说明:r相当于匿名函数的参数,当然也可以有多个参数,不用在写return,表达式就是返回的结果。
使用匿名函数有个好处,疑问函数没有名字,不用担心函数名冲突,此外匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量调用该函数。
5、关于函数的return语句
1)函数可以没有return语句,默认没有return语句返回的就None
2)return语句有点类似循环的break,当函数执行到return语句的时候将不会继续执行下去,直接跳出函数的执行,例如
def Foo6():
print('start')
return None
print('end') print(Foo6())
执行结果
start
None
说明:可以看到return语句后面的print('end')根本没有执行。
3)return可以返回多个值,接受的接收的可以使用两个变量接受,也可以一个变量接收
def Foo7():
return 123, 'abc' res1, res2 = Foo7()
print('res1:', res1)
print('res2:', res2) res = Foo7()
print('res:', res)
执行结果
res1: 123
res2: abc
res: (123, 'abc')
说明:可以返回多个值就是返回一个元祖,使用两个变量接收的时候回将元祖的元素与变量一一对应赋给多个变量,用一个变量接收的时候就接收了
6、关于可变参数和关键字参数的传递的小技巧
我们已经知道可变参数和关键字参数分别将传递的参数组装成元祖和字典,那么我们同样可以直接将元祖、列表和字典直接传递给函数作为参数,传递的时候列表和元祖要变量名要在前面加一个*,字典要在之前加两个**,否则函数还是会把把它们当成一个普通的参数传递进行处理
def Foo8(*args, **kwargs):
print(args)
print(kwargs) Foo8(li, dic)
Foo8(*li, **dic)
执行结果
([1, 2, 3], {'k1': 1, 'k2': 2})
{} # 可以看到两个参数都被可变参数接收了,关键字参数啥也没有
(1, 2, 3)
{'k1': 1, 'k2': 2}