1 Python中的函数
函数,从数学的角度来讲是,输入一个参数,经过一个表达式的处理后得到一个结果的输出,即就是x-->y的一个映射。同样,在Python或者任何编程语言中,函数其实就是实现一种功能,也可以称其为接口,通过使用定义的函数,以此来达到某种功能的实现。
1.1 Python中函数的定义
使用def语句可定义函数:
1 def 函数名(参数列表)
2 函数体(代码块)
3 [return 返回值]
函数名就是一个函数的名字,也是一种标识符,命名的要求为:只能以字母或下划线开头的除 Python的关键字外的任意字符串,注意Python是大小写敏感的;
Python中利用缩进的形式来表示语句块,约定4个空格;
返回语句,在一个函数中可以没有返回值,如果没有返回值,隐式会返回一个None值;
定义中的参数列表为形式参数,只是一种符号表达,简称形参;
比如下面的add函数:
1 def add(x, y):
2 return x + y
1.2 函数的调用
函数的定义过程,只是声明了一个函数,函数不会被执行,需调用。对于1.1中定义的函数,通过a = add(3, 4)来调用,其中调用时写的参数是实际参数,是实实在在传入的值,简称实参。
函数可调用的对象,可以通过callable()函数来检测一个变量是否可以调用。
2 函数参数
在Python中函数的参数有以下几种类型:
- 位置参数
- 关键字参数
- 可变参数
- keyword-only参数
2.1 位置参数
在1.1中定义的add(x, y)函数,其中x和y为位置参数。在调用函数的时候,实参的顺序和数量必须与函数定义中的形参匹配,否则会引发TypeError异常。
比如定义函数def f(x, y, z),调用时为f(1, 2, 3).
2.2 关键字参数
函数调用的过程当中,实参利用函数形参的名字进行赋值传参,那么该实参就为关键字参数。如果函数调用时的实参使用了形参名字,那么传参顺序就可和定义参数的顺序不同。
同样对函数的f有以下调用方式:
1 f(1, 2, 4)
2 f(z=None, x=6, y=4) # 关键字参数调用
3 f(y=5, 2, 6) # 该调用方式是否可行
注:要求位置参数必须在关键字参数之前传入,位置参数是按位置对应的。
2.3 可变参数
有时候在调用函数时,可能会给函数传多个参数,我们不可能在定义函数时将函数的参数的个数固定死,那么这个函数的灵活性就不存在。于是就有了可变参数,即一个形参可匹配任意个参数。可变参数分为:位置参数的可变参数和关键字参数的可变参数。
2.3.1 位置参数的可变参数
在形参前使用*标识该形参是可变参数,可以接收多个实参。如下例子:
1 def add(*nums):
2 sum = 0
3 print(type(nums))
4 for x in nums:
5 sum += x
6 print(sum)
7 add(3, 6, 9) # 调用
调用add函数时的实参为多个,这时形参中的*num会将多个实参收集为一个tuple。
2.3.2 关键字参数的可变参数
在形参前使用**符号,可以接收多个关键字参数。如下示例:
1 def showconfig(**kwargs):
2 for k,v in kwargs.items():
3 print('{} = {}'.format(k, v))
4 showconfig(host='127.0.0.1',port='8080',username='viktor',password='123456')
调用showconfig时使用了关键字参数,这时函数中的形参**kwargs会将多个关键字参数收集为一个字典(dict)。
2.3.4 可变参数总结
可变参数可以接收任意个参数,其中位置可变参数将收集的参数形成一个元组,关键字参数将收集的参数形成一个字典。当然这些参数都可以在函数定义时混合使用,混合使用参数的时候要注意:可变参数要放到参数列表的最后,位置参数放到参数列表的前面,位置可变参数需要放在关键字可变参数之前。如下例子:
1 def fn(x, y, *args, **kwargs):
2 print(x)
3 print(y)
4 print(args)
5 print(kwargs)
6 fn(3,5,7,9,10,a=1,b='python')
7 fn(3,5)
8 fn(3,5,7)
9 fn(3,5,a=1,b='python')
10 fn(7,9,y=5,x=3,a=1,b='python') # 错误,7和9分别赋给了x,y,又y=5、x=3,重复了
11
12 def fn(*args, x, y, **kwargs):
13 print(x)
14 print(y)
15 print(args)
16 print(kwargs)
17 fn(3,5) # 执行出现TypeError
18 fn(3,5,7) # 执行出现TypeError
19 fn(3,5,a=1,b='python') # 执行出现TypeError
20 fn(7,9,y=5,x=3,a=1,b='python')
View Code
2.4 keyword-only参数
keyword-only参数是在Python3中加入的。如果在一个星号参数后,或者一个位置可变参数后,出现普通参数,那么这个参数就为keyword-only参数。示例如下:
1 def fn(*args, x):
2 print(x)
3 print(args)
4 fn(3,5)
5 fn(3,5,7)
6 fn(3,5,x=7)
可以看出,在函数调用的过程中,args参数将所有的位置参数截获,x不使用关键字参数就不可能接收到实参的传递。那么,关键字的可变参数后是否能跟一个普通的位置参数呢?看如下示例:
1 def(**kwargs, x):
2 print(x)
3 print(kwargs)
运行后直接报语法错误,可以理解为kwargs会截获所有的关键字参数,就算在调用函数时的形参中有类似x = 5形式的传参,形参x也不能得到该值,而这个位置参数上的值又必须在函数调用时提供,所以就会出现语法错误。
keyword-only参数另一种形式:*号之后跟普通形参,示例如下:
1 def fn(*, x,y):
2 print(x,y)
3 fn(x=5,y=6)
*号之后,普通参数都会变成必须给出的keyword-only参数。
2.5 可变参数和参数默认值
函数的定义中,将参数的默认值和可变参数混合在一起使用,那么函数的调用过程中是如何传参呢?看下面几个示例:
1 def fn(y, *args, x=5):
2 print('x={}, y={}'.format(x, y))
3 print(args)
4 fn() # 出错,因为fn函数中的位置参数y在调用fn时必须得给出;
5 fn(5) # 可以执行,此时形参y接收5,args参数为空,x参数使用默认值;
6 fn(x=6) # 错误,形参y必须接收一个实参;
7 fn(1,2,3,x=10) # 可以执行,此时y=1,args=(2, 3),x=10;
8 fn(y=17,2,3,x=10) # 出现语法错误,调用函数时,必须得将关键字参数放到参数列表的后面
9 fn(1,2,y=3,x=10) # 出错,形参y已经接收到实参1,之后又利用关键字参数给y进行传参,肯定会出错
1 def fn(x=5, **kwargs):
2 print('x={}'.format(x))
3 print(kwargs)
4 fn() # 可以执行,x使用默认值5,kwargs为空
5 fn(5) # 可以执行,x=5,kwargs为空
6 fn(x=6) # 可以执行,此时x=6,kwargs为空
7 fn(y=3,x=10) # x=10,字典中存入'y':3
8 fn(3,y=10) # x=3,字典中存入'y':10
2.6 函数参数定义规则
参数列表中的参数的一般顺序是:普通参数、缺省参数、可变位置参数、keyword-only参数(可带缺省值)、可变关键字参数。如下:
1 def fn(x, y, z=3, *arg, m=4, n, **kwargs):
2 print(x,y,z,m,n)
3 print(args)
4 print(kwargs)
3 函数参数解构
给函数提供实参时,可以在集合类型的实参前使用*或者**,把集合类型的结构解开,提取出所有元素作为函数的实参。如下示例:
1 def add(x, y):
2 return x+y
3
4 add(4, 5)
5 add((4,5))
6 t = (4, 5)
7 add(t[0], t[1])
8 add(*t)
9 add(*(4,5))
10 add(*[4,5])
11 add(*{4,6})
12 add(*range(1,3))
13
14 d = {'x': 5, 'y': 6}
15 add(**d)
16 add(**{'a': 5, 'b': 6})
17 add(*{'a': 5, 'b': 6})
18
19 def add(*iterable):
20 result = 0
21 for x in iterable:
22 result += x
23 return result
24 add(1,2,3)
25 add(*[1,2,3])
26 add(*range(10))
View Code
参数解构时,非字典类型的实参使用*解构成位置参数;字典类型使用**解构成关键字参数。注意:提取出来的元素数目要和参数的要求匹配,也要和参数的类型匹配。