5.3 函数参数
示例2中定义的函数虽然能够计算总分数,但是每次计算后的结果是固定的,为了增加函数的灵活性,我们希望在调用函数的时候可以传入一些数据,然后根据传入的数据进行计算。要满足这种需求,就需要在定义函数的时候定义参数。
5.3.1 形参和实参
在编程语言中,函数定义使用的是形参
,调用时传入的是实参
。
形参(parameter)
,全称为形式参数
,不是实际存在的变量,又称虚拟变量。形参是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数。
实参(argument)
,全称为实际参数
,是在调用时传递给函数的参数。实参可以是常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。
形参和实参的功能是数据传送。
在调用函数时,实参将赋值给形参。必须注意实参的个数、类型应与形参要一一对应,并且实参必须要有确定的值。
形参的作用域一般仅限函数体内部,而实参的作用域根据实际设置而定。
示例3:通过传入数据计算总分数
|
def sum_score3(a, b, c): |
|
return a + b + c |
|
|
|
|
|
sumScore = sum_score3(88, 66, 95) |
|
print(sumScore) |
249
上面的示例3中,在定义函数sum_score3
时,a
、b
、c
就是形参,而在调用函数时,传入的88
、66
、95
就是实参。 在调用函数时,执行代码时会将实参的88、66、95分别传入到形参a、b、c中,此后,a = 88,b = 66,c = 95,然后在执行函数体的代码时,就得到了a + b + c的值并返回。
5.3.2 位置参数(顺序定义和调用)
调用函数时,Python语言必须将函数调用中的每个实参都关联到函数的相应形参。最简单的关联方式是基于实参的顺序,这种关联方式被称为位置参数。 位置形参
:在定义函数时,按照从左到右的顺序依次定义形参 位置实参
:在调用函数时,按照从左到右的顺序依次传入实参 示例4:通过位置参数输出学生的成绩
|
def sum_score4(a, b, c): |
|
print(f'语文:{a},数学:{b},英语{c}') |
|
|
|
|
|
sum_score4(88, 66, 95) |
语文:88,数学:66,英语95
对于位置参数,在调用函数时,必须按照顺序输入全部的实参。实参的数量不能多也不能少,顺序也必须和形参保持一致。 以下为错误的调用方式(假设该学生的语文、数学和英语的成绩分别为:88、66、95):
|
sum_score4(88, 66) ×:少输入了一个参数 |
|
sum_score4(88, 66, 95, 100) ×:多输入了一个参数 |
|
sum_score4(66, 88, 95) ×:实参的顺序错了,会导致输出后语文变成66 |
5.3.3 关键字实参(函数调用时)
对于上面的位置参数,在调用函数时,必须按照顺序输入所有的实参,在函数调用时不是很方便。所以一般会通过关键字参数输入实参。 示例5:通过关键字实参调用函数
|
def sum_score5(chinese, math, english): |
|
print(f'语文:{chinese},数学:{math},英语{english}') |
|
|
|
|
|
sum_score5(chinese=88, math=66, english=95) |
语文:88,数学:66,英语95
这种通过关键字参数的方式传入实参从而调用函数,就不用担心参数的顺序问题了。我们在定义函数时,形参的命名必须做到望文生义,这样在通过关键字传入实参时才不容易出错。 注意: 函数传参时,某个形参通过关键字传参时,后面的所有形参也必须通过关键字传参(关键字传参必须在位置传参之后)。 以下为错误的关键字参数传入方式: >>> sum_score5(chinese=88, 66, 95) × >>> sum_score5(88, math=66, 95) × 以下为正确的关键字参数传入方式: >>> sum_score5(88, math=66, english=95) √ >>> sum_score5(88, 66, english=95) √
5.3.4 默认值形参(函数定义时)
如果我们现在要定义一个函数,用来输出学生的姓名、年龄和性别。如果按照上面的位置参数方式来定义,需要有三个形参,分别是name、age和gender。每次调用函数时,必须传入学生的姓名、年龄和性别。 如果大部分的学生年龄都是18岁,那么我们可以设置一个默认值形参,函数定义如下: 示例6:定义函数时为形参设置默认值
|
def showinfo(name, gender, age=18): |
|
print(f'姓名:{name},年龄:{age},性别:{gender}') |
|
|
|
|
|
showinfo('张三', '男') |
|
showinfo('张三', gender='男') # 关键字实参 |
|
showinfo('张三', '男', 20) |
姓名:张三,年龄:18,性别:男 姓名:张三,年龄:18,性别:男 姓名:张三,年龄:20,性别:男
上面定义函数showinfo
时,为形参age设置了默认值,在调用此函数时,如果对形参age
不传入值,那么形参age
的值默认为18,否则为传入的值。 通过形参设置默认值可以简化函数的调用。 注意: 1、函数参数定义时,如果某个形参设置了默认值,那么其后面所有的形参也必须设置默认值(默认值形参必须在位置形参之后)。 2、默认值形参设置的默认值最好设置成可哈希数据(不可变)。默认值只被赋值一次。这使得当默认值是可变对象时会有所不同,比如列表、字典或者大多数的实例。例如,下面的函数在后续调用过程中会累积(前面)传给它的参数:
|
def f(a, L=[]): |
|
L.append(a) |
|
return L |
|
print(f(1)) |
|
print(f(2)) |
|
print(f(3)) |
[1] [1,2] [1,2,3]
如果你不想让默认值在后续调用中累积,你可以像下面一样定义函数:
|
def f(a, L=None): |
|
if L is None: |
|
L = [] |
|
L.append(a) |
|
return L |
|
print(f(1)) |
|
print(f(2)) |
|
print(f(3)) |
[1] [2] [3]
5.3.5 特殊形参(函数定义时)
5.3.5.1 收集位置形参
在函数定义时,如果形参名前面带有*
号,则这个形参可以在函数调用时收集所有的位置实参。一般用*args
形参来收集。 示例7:收集位置形参
|
def collect_args(name, age, *args): |
|
print(f'姓名:{name}, 年龄:{age}') |
|
print(f'各科成绩:{args}') |
|
|
|
|
|
collect_args('启明', 12, 85, 66, 100) |
姓名:启明, 年龄:12 各科成绩:(85, 66, 100)
上面的函数通过形参*args
收集所有的科目成绩。收集后会放入到一个名为args
的元组中。 注意: 1、特殊形参*args
之前可以有若干个位置形参或默认值形参。但是之后不能。 2、特殊形参*args
只能收集位置实参,对于调用时传入的关键字实参则不会收集。
5.3.5.2 收集关键字形参
在函数定义时,如果形参名前面带有**
号,则这个形参可以在函数调用时收集所有的关键字实参。一般用**kwargs
形参来收集。 示例8:收集关键字形参
|
def collect_kwargs(name, age=18, **kwargs): |
|
print(f'姓名:{name}, 年龄:{age}') |
|
print(f'特殊科目成绩:{kwargs}') |
|
|
|
|
|
collect_kwargs('启明', 12, chinese=86, math=88) |
姓名:启明, 年龄:12 特殊科目成绩:{'chinese': 86, 'math': 88}
上面的函数通过形参**kwargs
收集所有的科目成绩。收集后会放入到一个名为kwargs
的字典中。 注意: 1、特殊形参**kwargs
只能放到位置形参、关键字形参、*args形参之后。 2、特殊形参**kwargs
只能收集关键字实参,对于调用时传入的位置实参则不会收集。
5.3.5.3 仅位置参数、仅关键字参数
在Python定义函数时,形参如果使用/
则代表前面的形参仅能通过位置参数传递。 在Python定义函数时,形参如果使用*
则代表后面的形参仅能通过关键字参数传递。 def fun(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2): | | | | 位置参数或者关键字参数 | | 仅关键字参数 仅位置参数
5.3.6 形参组合
在Python定义函数时,如果需要使用多种形参:位置、默认、可变参数、关键字参数等。在定义时是有顺序的。定义的顺序必须是:位置形参、默认形参、可变位置形参、可变关键字形参。比如要定义一个函数,包含上述若干种形参,如下代码所示:
|
|
|
def func(a, b, c=0, *args, **kwargs): |
|
print('a=', a, 'b=', b, 'c=', c, 'args=', args, 'kwargs=', kwargs) |
5.3.7 参数解包
我们在之前的第四章可迭代对象的通用操作
中学习了解包
和装包
。当时介绍了可以通过解包的方式将某个可迭代对象作为多个实参传入到函数中。利用这种方式作为函数的参数称之为参数解包
。 参数解包示例1
|
def args_unpacking1(a, b, c): |
|
print(a, b, c, sep='-') |
|
|
|
|
|
lst = [1, 2, 3] |
|
args_unpacking1(*lst) |
1-2-3
参数解包示例2
|
def args_unpacking1(*args): |
|
print('-'.join(args)) |
|
|
|
|
|
lst = 'python' |
|
args_unpacking1(*lst) |
p-y-t-h-o-n
参数解包示例3
|
def args_unpacking1(a, b): |
|
print(a, b, sep='-') |
|
|
|
|
|
dct = {'a': 11, 'b': 22} |
|
args_unpacking1(**dct) |
11-22
参数解包示例4
|
def args_unpacking1(**kwargs): |
|
print(kwargs) |
|
|
|
|
|
dct = {'a': 11, 'b': 22} |
|
args_unpacking1(**dct) |
{'a': 11, 'b': 22}
总结: 1、通过*
解包可迭代对象后可以作为位置实参传入函数中。而函数可以通过位置形参或者*args
形参来收集。 2、通过**
解包字典后可以作为关键字实参传入函数中。而函数可以通过默认值形参或者**kwargs
形参来收集。