一、函数分类
(1)内置函数。Python语言内置了若干常用的函数,例如abs()、len()等等,在程序中可以直接使用。
(2)标准库函数。Python语言安装程序同时会安装若干标准库,例如math、random等等。通过import语句,可以导入标准库,然后使用其中定义的函数。
(3)第三方库函数。Python社区提供了许多其他高质量的库,如Python图像库等等。下载安装这些库后,通过import语句,可以导入库,然后使用其中定义的函数。
(4)用户自定义函数。本章将详细讨论函数的定义和调用方法。
二、函数基本形式
def 函数名([参数列表]):
‘’‘注释’’’
函数体
例如:
def main():
print('hello')
main()
注意事项
函数形参不需要声明类型,也不需要指定函数返回值类型
即使该函数不需要接收任何参数,也必须保留一对空的圆括号
括号后面的冒号必不可少
函数体相对于 def 关键字必须保持一定的空格缩进
Python允许嵌套定义函数
三、形参与实参
1、定义理解
形参出现在函数定义中,在整个函数体内都可以使用, 离开该函数则不能使用。
实参出现在主调函数中,进入被调函数后,实参变量也不能使用。
1、形参只是在函数定义时使用;
2、实参是在函数调用时赋予的实际数值;
def ad(x,y): #此时x,y是形参,ad是被调函数
c = x + y
return c
def main():
a=1 #此处a,b是实参
b=2 #此处a,b是实参
d = ad(a,b) #此处a,b是实参,a.b在主调函数main中可以使用,但在被调函数ad中只能用x,y
print(d)
main()
函数定义时小括号内为形参,形参不需要声明类型,一个函数可以没有形参,但是小括号必须要有,表示该函数不接受参数。参数个数并没有限制,若有多个形参,需要使用逗号进行分割。
函数调用时向其传递实参,将实参的值或引用传递给形参。
2、改变形对实参的影响
(1)对于绝大多数情况下,在函数内部直接修改形参的值不会影响实参。
样例:
def change(n):
n+=1
print('a通过函数change改变后的值:',n)
def main():
a=1
print('a的初始默认值:',a)
change(a)
print('验证a的实参值是否被函数中形参所影响:',a)
main()
运行结果是:
a的初始默认值: 1
a通过函数change改变后的值: 2
验证a的实参值是否被函数中形参所影响: 1
可见函数内部直接修改形参的值不会影响实参
(2)如果传递给函数的是可变序列,并且在函数内部使用下标或可变序列自身的方法增加、删除元素或修改元素时,修改后的结果是可以反映到函数之外的,实参也得到相应的修改。
样例:
def change(n):
b=[4,5,6]
n.extend(b)
print('a通过函数change改变后的值:',n)
def main():
a=[1,2,3]
print('a的初始默认值:',a)
change(a)
print('验证a的实参值是否被函数中形参所影响:',a)
main()
运行结果是:
a的初始默认值: [1, 2, 3]
a通过函数change改变后的值: [1, 2, 3, 4, 5, 6]
验证a的实参值是否被函数中形参所影响: [1, 2, 3, 4, 5, 6] #程序猿用事实说话,懂了吧!
四、参数类型
在 Python 中,函数参数有很多种:可以为普通参数、默认值参数、关键字参数、可变长度参数等等。
Python 在定义函数时不需要指定形参的类型,完全由调用者传递的实参类型以及 Python 解释器的理解和推断来决定,类似于重载和泛型。
Python 函数定义时也不需要指定函数返回值的类型,这将由函数中的 return 语句来决定,如果没有 return 语句或者 return 没有得到执行,则认为返回空值 None。
仅仅是不需要指定,并非完全不能指定,Python支持对函数参数和返回值类型的标注,但实际上并不起任何作用,只是看起来方便。
例如:
def change(n:str)->str: #此处俩种指定“:str”和“->str”都是错误的,n应该是int型才对,但这不影响结果,不会出异常。
n+=1
print('a通过函数change改变后的值:',n)
def main():
a=1
print('a的初始默认值:',a)
change(a)
print('验证a的实参值是否被函数中形参所影响:',a)
main()
运行结果:
a的初始默认值: 1
a通过函数change改变后的值: 2
验证a的实参值是否被函数中形参所影响: 1
1、位置参数
位置参数(positional arguments)是比较常用的形式,就是指调用函数时实参和形参的顺序必须严格一致,并且实参和形参的数量必须相同。
函数按位置传递参数
def change(a,b,c):
print(a,b,c)
def main():
a=1
b=2
c=3
change(a,c,b) #形参按实参传入位置来顺序获取值
main()
所以结果是:
1 3 2
实参与形参数量若不相同报异常
def change(a,b,c,d): #多出一个d形参
print(a,b,c)
def main():
a=1
b=2
c=3
change(a,c,b)
main()
因为形参多了一个而实参未能满足,所以结果是:
change(a,c,b)
TypeError: change() missing 1 required positional >argument: 'd'
2、默认值参数
1、默认值参数位置问题
默认值参数必须出现在函数参数列表的最右端,且任何一个默认值参数右边不能有非默认值参数。
(1)错误样例就是最右端不是默认值参数:
def change(a,b=0,c,d): #默认值参数b不在最右端
print(a,b,c,d)
def main():
a=1
b=2
c=3
d=4
change(a,b,c,d)
main()
结果是:
def change(a,b=0,c,d):
^
SyntaxError: non-default argument follows default argument
其实我认为这里可能是因为涉及后面要讲的默认值参数可赋值可不赋值,到时不赋值默认值参数,默认值参数若不放在最后就会出现上面的位置参数问题,不好对应。
(2)正确样例1:(最右端是默认值参数)
def change(a,b,c,d=0): #默认值参数d在最右端
print(a,b,c,d)
def main():
a=1
b=2
c=3
d=4
change(a,b,c,d)
main()
结果:
1 2 3 4
正确样例2:(默认值参数右端都是默认值参数)
def change(a,b=3,c=2,d=0): #默认值参数b的右端都是默认值参数
print(a,b,c,d)
def main():
a=1
b=2
c=3
d=4
change(a,b,c,d)
main()
结果:
1 2 3 4
2、理解默认值参数只在函数定义时被解释一次
(1)首先理解默认值参数就是已经有默认值了,可以不传给它实参
例如:
def change(a,b=3):
b+=a
return b
def main():
print(change(1)) #这里只给a传了值,b有默认值
main()
结果是:
4
(2)python解释器对于默认值参数的赋值只会在函数定义时被解释一次。
也可以理解为默认值只存在存储区一次,谁先用了就归谁。
错误样例:
def change(a,b=[1,2,3]): # b[2]的默认值为3
b[2]+=a
return b[2] # 该函数功能就是求a+b[2]的和
def main():
print('第一次结果:',change(1,[1,2,2])) #本意是求1+2
print('第二次结果:',change(3,[1,2,4])) #本意是求3+4
print('第三次结果:',change(5)) #本意是求5+3
print('第四次结果:',change(2)) #本意是求2+3
main()
结果如下:
第一次结果: 3
第二次结果: 7
第三次结果: 8
第四次结果: 10
理解:
*第一次print中为b赋值了,没用b的默认值,在被调函数change中b直接使用被赋的值2,保留默认值3。即python解释器不需要对b的默认值进行解释。所以直接计算1+2,结果正确
*第二次print中也为b赋值了,仍没用b的默认值,在被调函数change中b使用被赋的值4,保留默认值3。即python解释器不需要对b的默认值进行解释。所以直接计算3+4,结果正确
*第三次print中没对b赋值,b需要使用自身默认值,在这里默认值被用掉,即python解释器对b的默认值进行了唯一的一次解释,之后不在解释,也就是b没有了默认值,或者说是b之后的默认值就是b的值,会随函数的改动而改动,毕竟之后还是可以对b不传值的,b也还是默认值参数。这里计算的就是5+3的值,结果正确
*第四次print中没对b赋值,也就如上面所说b没有了默认值,或者说是b之后的默认值就是b的值,会随函数的改动而改动。因此此处b的默认值就是上次函数调用结束时b的值,上次b+=a后b是8,所以这里计算的是8+2,与我们预期的算3+2不符,结果错误。
若想达到预期,需将代码改为:
def change(a,b=None): #这里只能是None,不能写b=[ ]
if b is None: #这里也一样,不能是[ ]
b=[1,2,3]
b[2]+=a
return b[2]
def main():
print('第一次结果:',change(1,[1,2,2]))
print('第二次结果:',change(3,[1,2,4]))
print('第三次结果:',change(5))
print('第四次结果:',change(2))
main()
结果如下:
第一次结果: 3
第二次结果: 7
第三次结果: 8
第四次结果: 5
需要注意这种错误仅对应于可变序列,对于不可变或实数不成立
举例实数:
def change(a,b=3): # b的默认值为3
b+=a
return b # 该函数功能就是求a+b[2]的和
def main():
print('第一次结果:',change(1,2)) #本意是求1+2
print('第二次结果:',change(3,4)) #本意是求3+4
print('第三次结果:',change(5)) #本意是求5+3
print('第四次结果:',change(2)) #本意是求2+3
main()
结果如下:
第一次结果: 3
第二次结果: 7
第三次结果: 8
第四次结果: 5
可见对于不可变序列默认值始终不变,这就像是形参与实参中的可变序列的形参改变会影响到实参一样,在这里可变序列的改变会影响到它的默认值。
类似可变默认值,实则不是的例子:
def main():
x=2
def change(a,b=x): #在这里b就已经将默认值设为2,
#看起来像是b的默认值随x变化而变化,
#其实这里类似与(a,b=2),是不可变的。
b+=a
return b
x=4
print('第一次结果:',change(2,x))
print('第二次结果:',change(2))
print('第三次结果:',change(2))
main()
运送结果:
第一次结果: 6
第二次结果: 4
第三次结果: 4
第一次赋值了,而且x的值改为了4,第一次结果与预期一致。
第二次使用了默认值,由结果可知在函数定义时b的默认值已确定,在x改变后b的默认值未变。
有第三次结果可知b的默认值没有在第二次运算中改变,就是相当于不可变默认值。
3、关键字参数
(1)普通关键字参数
关键字参数主要指实参,即调用函数时的参数传递方式。
通过关键字参数,参数按名称传递意义明确;传递参数时与形参顺序无关;如果有多个可选参数,则可以选择指定某个参数值。
对比以下俩代码:
样例1
def change(a,b,c):
print(a,b,c)
def main():
x,y,z=1,2,3
change(a=x,b=y,c=z) #按序输入
main()
结果是:
1 2 3
样例2
def change(a,b,c):
print(a,b,c)
def main():
x,y,z=1,2,3
change(a=x,c=z,b=y) #对形参b,c的输入顺序改变
main()
结果是:
1 2 3
由此可见有关键字以后,可以直接对形参指定赋值,不必再依照位置参数。
(2)强制关键字参数
普及参数及变量前加参数:
1、函数声明的参数列表中加单星号,即f(*x)则表示x为元组,所有对x的操作都应将x视为元组类型进行。所有传入f(*x)的变量都将作为元组x的元素之一。
2、双星号同上,区别是x视为字典。
3、在变量前加单星号表示将元组(列表、集合)拆分为单个元素。
4、双星号同上,区别是目标为字典,字典前加单星号的话可以得到“键”。
在带星号的参数后面声明的参数强制为关键字参数,如果这些参数没有默认值,则调用时必须使用关键字参数赋值,否则会引发错误。
如果不需要带星号的参数,只需要强制关键字参数,则可以简单地使用一个星号,如 def total(initial=5, *, vegetables)
来用几个样例说明这段话:
样例1(星号后使用关键字参数赋值):
def change(a,*b,c,d,e): #*b既将被视为元组,也将c,d,e强制为关键字参数
print(a,b,c,d,e) #b直接输出是元组形式
def main():
x,y,z,s,t=1,2,3,4,5
change(x,y,c=z,d=s,e=t) #c,d,e使用关键字参数赋值
main()
运行结果:
1 (2,) 3 4 5
样例2(星号后未用关键字参数赋值):
def change(a,*b,c,d,e): #*b既将被视为元组,也将c,d,e强制为关键字参数
print(a,b,c,d,e) #b直接输出是元组形式
def main():
x,y,z,s,t=1,2,3,4,5
change(x,y,z,d=s,e=t) #c未使用关键字参数赋值,是按照位置参数赋值的
main()
结果异常:
change(x,y,z,d=s,e=t)
TypeError: change() missing 1 required keyword-only argument: 'c'
通过样例1,2可知星号后被强制关键字的参数必须用关键字参数赋值
样例3(星号后有默认值参数,用关键字参数赋值,对默认值参数不赋值):
def change(a,*b,c,d=999,e): #*b既将被视为元组,也将c,d,e强制为关键字参数,d是默认值参数
print(a,b,c,d,e) #b直接输出是元组形式
def main():
x,y,z,s,t=1,2,3,4,5
change(x,y,c=z,e=t) #c,e使用关键字赋值,默认值参数d未赋值
main()
运行结果:
1 (2,) 3 999 5
样例3中可知虽然被强制为关键字参数但默认值参数还是可以不赋值。
但还有一个发现,那就是被强制关键字参数后,默认值参数不必再位于最右端。
样例4(星号后有默认值参数,用关键字参数赋值,对默认值参数用位置参数赋值):
def change(a,*b,c,d=999,e): #*b既将被视为元组,也将c,d,e强制为关键字参数,d是默认值参数
print(a,b,c,d,e) #b直接输出是元组形式
def main():
x,y,z,s,t=1,2,3,4,5
change(x,y,c=z,s,e=t) #c,e使用关键字赋值,默认值参数d用位置参数赋值
main()
运行结果:
change(x,y,c=z,s,e=t)
^
SyntaxError: positional argument follows keyword argument
样例5(星号后有默认值参数,用关键字参数赋值,对默认值参数用关键字参数赋值):
def change(a,*b,c,d=999,e): #*b既将被视为元组,也将c,d,e强制为关键字参数,d是默认值参数
print(a,b,c,d,e) #b直接输出是元组形式
def main():
x,y,z,s,t=1,2,3,4,5
change(x,y,c=z,d=s,e=t) #c,e使用关键字赋值,默认值参数d用关键字参数赋值
main()
运行结果:
1 (2,) 3 4 5
由样例3、4、5可知,对于被强制为关键字参数的默认值参数可以不传值,但如果要赋值也只能用关键字参数赋值。
样例6(理解不需要带星号的参数,只需要强制关键字参数,则可以简单地使用一个星号):
def change(a,*,c,d=999,e): # *之后的c,d,e都被强制为关键字参数,没有了*b,也就是不需要元组,只需牵制关键字参数
print(a,c,d,e) #直接输出的结果中将不再有元组形式
def main():
x,y,z,s,t=1,2,3,4,5
change(x,c=z,d=s,e=t) # *之后的都采用了关键字赋值
main()
运行结果:
1 3 4 5
4、可变长度参数
可变长度参数主要有两种形式:
*参数用来接受多个实参并将其放在一个元组中
**参数用来接受关键字参数并存放到字典中
这个在上面强制关键字参数中有提及,*不光会将之后的参数强制为关键字参数,也会将它贴着的参数视为元组,上面样例中有。
下面讲讲如何将多个实参传入这种元组或字典中,
这里要注意,由于强制关键字参数的缘故,可变长度参数要放在最右端。
def change(a,b,*c): #*c既将被视为元组,即可变长度参数
print(a,b,c) #c直接输出是元组形式
def main():
x,y=1,2
change(x,y,1,2,3,4,5) #前面x、y以位置参数的方式对a、b赋值,后面的多个实参都将作为元组c的元素
main()
运行结果:
1 2 (1, 2, 3, 4, 5)
def change(a,b,*c,**d): #*c既将被视为元组,d被视为字典,即可变长度参数
print(a,b,c,d) #c直接输出是元组形式,d以字典形式输出
def main():
x,y=1,2
change(x,y,1,2,3,4,5,r=11,s=22,t=33) # =形式的参数被识别为字典元素
main()
运行结果:
1 2 (1, 2, 3, 4, 5) {'r': 11, 's': 22, 't': 33}
补充一下:虽然字典元组可以混用,但不鼓励用,还有就是若没给例如=形式的参数,输出的字典将显示空{},不会错。