Python中的函数参数
函数是具有一个功能或者多个功能的代码块,函数可以解决大量代码重复、功能不清晰等问题,分为内置函数和自定义函数。
函数的作用:
- 通过函数,可以对功能进行详细的划分,在需要使用功能时,就可以调用该函数。
- 通过函数,可以避免代码的重复,实现代码的重用。
函数的分类:
- 内置函数
- 自定义函数
1.内置函数
print
input
abs
max
min
其他见过的类型转换函数等
2.自定义函数
自定义函数的定义方式如下:
def 函数名(参数列表):
函数体
实现一个每行打印5 个* ,一共打印5行的函数:
# 定义函数
def printStar():
for i in range(5):
print("*"*5)
# 调用函数
printStar()
函数被定义之后只有调用了才能执行。
2.1 关键字pass的使用
在程序设计时,我们可以先把程序划分成若干个功能,具体的实现可以后续再完成。可是,在Python
中,函数不允许没有函数体,此时,我们就可以使用pass
来进行占位。pass表示空语句,用在函数中用来进行占位。不过,pass也可以放在选择与循环体中。
示例:
def a():
pass
def b():
pass
2.2 函数的参数
如果把函数比喻成一台加工机器,参数就是被加工的原材料。在函数的定义中参数可以看作是一个占位符,它没有数据,只能等待函数被调用的时候接受传递进来的数据。
3.函数参数类型
3.1 位置参数
调用函数时根据函数定义的参数位置来传递参数。一旦定义了位置参数,除默认参数以外,必须传入位置参数,而且必须按照定义的顺序传入。
示例:
def sum(a,b,c):
return a+b+c
sum(10,20,30)
# output:
# 60
3.2 默认值参数
如果形参含有默认值,则在函数定义时,含有默认值的形参一定要定义在不含默认值的形参之后。
这是因为,如果形参含有默认值,则意味着,该参数是一个可选参数,在函数调用时,可以选择性的进行传值。如果允许含有默认值的形参定义在不含默认值的形参前面,就会造成混淆。
示例:
def f( length=3,width, height=4):
print("length=",length)
print("width=",width)
print("heiht=",height)
f(5, 8)
# output:
# SyntaxError: non-default argument follows default argument
注意不要使用不可变对象作为默认值。
3.3 命名关键字参数
除了位置参数之外,还可以使用命名关键字参数。所谓命名关键字参数,就是在调用函数时,显式指定参数名=参数值
的形式,即指定要给哪一个形参传递值。同时,也可以在调用函数时,混合使用位置参数与关键字参数,但是位置参数放在前面。
命名关键字参数的作用很多,总结来说,命名关键字参数具有如下优势:
- 命名关键字参数能够增加程序的可读性。
- 命名关键字参数可以不用考虑传递的顺序。
- 当多个形参存在默认值时,命名关键字参数能够指定赋值给哪个形参。
①增加程序的可读性,假设如下的函数:
def box_volume(length, width, height):
我们可以使用位置参数调用:
box_volume(10, 20, 30)
这样是没有问题的,不过,当参数较多的时候,可能会降低程序的可读性,因为我们需要确定每个参数具体代表的含义(此时可能就得重新查看一下函数的定义)。但是,使用命名关键字参数进行调用,就会变得清晰,增加了程序的可读性:
box_volume(length=10, width=20, height=30)
②不用考虑传递顺序:
如果使用位置参数,则必须严格要求按照顺序进行传递,如果使用命名关键字参数,则无此限制,因为完全可以任意颠倒参数的顺序:
box_volume(width=20, length=10, height=30)
③指定为哪一个形参赋值:
当函数存在多个默认值时:
def box_volume(length=3, width=4, height=5):
如果我们使用位置参数进行调用:
box_volume(10)
则实际参数10会赋值给length
,然而,我们的原意未必总是想指定length
,如果我们想指定width
或height
,而让另外两个参数取默认值,或者想显式指定其中的两个,另外一个取默认值,此时就可以使用命名关键字参数来实现需求了。
box_volume(width=10)
在进行命名参数传递时需要注意以下几点:
- 位置参数必须出现在命名关键字参数的前面。
- 同一个参数不能传递多次。(位置参数与命名关键字参数,或多个命名关键字参数)
- 命名关键字参数的顺序可以颠倒,但是提供的参数名必须存在。
强制使用命名关键字参数:
之前说过,使用命名关键字参数可以显式指定实参要赋值给哪一个形参,这样就可以提高程序的可读性。通常情况下,对于可选的参数(含有默认值的参数),因为其存在的不确定性,因此如果需要为可选参数传值,在调用时往往使用关键字参数是一种好的选择。不过,到底使用位置参数,还是命名关键字参数,完全取决于函数的调用者,函数的定义者并没有能力进行控制。也就是说,程序员A在调用函数时,可能会使用关键字参数,而程序员B却使用位置参数。为了能够增强程序的可读性,同时实现编程风格的一致性,函数的定义者可以使用*
语法,来限制*
后面的所有参数必须都是以关键字参数的形式传递。
如:
def 函数名(* , 参数名1,参数名2)
说明:
强制使用关键字往往针对的是具有默认值的参数,但从语法的角度讲,对于不含默认值的参数,也是可以的。
3.4 可变参数
定义函数时,有时候不确定调用的时候会传递多少个参数 (不传参也可以)。此时,可用包裹packing
位置参数,或者包裹关键字参数,来进行参数传递,会显得非常方便。可变参数*
,也可以叫收集参数。
函数调用时,位置可变参数(形参)可以用来接收所有未匹配的位置参数(实参)并封装成元组传递给函数。
def func(*args):
print(type(args))
print(*args)
func(10,20,"str")
# output:
# <class 'tuple'>
# 10 20 str
传进的所有参数都会被args
变量收集,它会根据传进参数的位置合并为一个元组tuple
,args
是元组类型,这就是包裹位置传递。函数调用的时候,使用*
对参数进行拆包,*
可以对元组进行拆包:
t = (1,2,3)
print(*t)
# output:
# 1 2 3
3.5 关键字可变参数
关键字可变参数(形参)可以用来接收所有未匹配的关键字参数(实参)。所有未匹配的关键字可变参数会打包成一个字典。
对于关键字可变参数,可以用来接收一些可选的选项,设置等。对于选项较多时,如果将每个选项都用一个关键字参数来接收时,会显得函数定义特别庞大,此时就可以使用一个关键字可变参数来实现。
例如,在Requests
库中,get
函数定义为:
requests.get(url, params=None, **kwargs)
其中,kwargs
是一个字典,可以接收很多选项,例如data
,headers
,cookies
,files
等。在调用的时候可以将字典拆包成命名关键字参数。
示例:
def func(a,b,**kwargs):
print(type(kwargs))
print(kwargs)
for i in kwargs.items():
print (i)
func(10,20,c=5,d=10)
# output:
# <class 'dict'>
# {'c': 5, 'd': 10}
# ('c', 5)
# ('d', 10)
对于关键字可变参数需要注意如下事项:
- 可变参数只能接收未匹配的(剩下的)实际参数,如果实际参数已经匹配某形式参数,则不会由可变参数接收。
- 在参数位置上,关键字可变参数必须位于函数参数列表的最后。
- 在位置可变参数后面定义的参数,将自动成为关键字参数。(为什么)
- 位置可变参数与关键字可变参数各自最多只能定义一个。
3.6 参数组合
位置参数,默认参数,命名关键字,可变参数,关键字可变参数可以组合使用,可变参数和关键字可变参数不可混合(两个单独带*的)。
其余参数的顺序必须是:
def func(必选参数,默认参数,命名关键字参数/ *args, **kwargs)
4.可变类型参数传递与不可变类型参数传递参数传递
不可变数据类型在第一次赋值声明的时候会在内存中开辟一块空间存放这个变量被赋的值, 而这个变量实际上存储的并不是被赋予的这个值,而是存放这个值所在空间的内存地址。通过这个地址变量就可以在内存中取出数据了。所谓不可变就是说不能改变这个数据在内存中的值,所以当我们改变这个变量的赋值时,实际的操作是在内存中重新开辟了一块空间,然后将新的数据存放在这一个新的内存空间里,而原来的那个变量就不在引用原数据的内存地址而转为引用新数据的内存地址了。
不可变类型的有:数字(如int,double)、字符串、元组。
可变数据类型是指变量所指向的内存地址处的值是可以被改变的。
可变类型有:集合、列表、字典。
不变可变类型的值传递,当调用函数后不会将不可变类型的对象的值改变:
def myfun(a):
a=a+2
print("在myfun的函数中a=",a)
a=3
myfun(a)
print("在主程序中a=",a)
# output:
# 在myfun的函数中a= 5
# 在主程序中a= 3
可变类型的值传递,当调用函数后,会改变可变类型对象的值:
def myfun(a):
a[0]="new"
print("在myfun函数中a",a)
a=[1,2,3,4]
myfun(a)
print("在主程序中a=",a)
# output:
# 在myfun函数中a ['new', 2, 3, 4]
# 在主程序中a= ['new', 2, 3, 4]