函数的参数
形参与实参介绍
函数的参数分为形式参数和实际参数,简称形参和实参:
形参即在定义函数时,括号内声明的参数。形参本质就是一个变量名,用来接收外部传来的值。
实参即在调用函数时,括号内传入的值,值可以是常量、变量、表达式或三者的组合:
#1:实参是常量
res=my_min(1,2)
#2:实参是变量
a=1
b=2
res=my_min(a,b)
#3:实参是表达式
res=my_min(10*2,10*my_min(3,4))
#4:实参可以是常量、变量、表达式的任意组合
a=2
my_min(1,a,10*my_min(3,4))
在调用有参函数时,实参(值)会赋值给形参(变量名)。在Python中,变量名与值只是单纯的绑定关系,而对于函数来说,这种绑定关系只在函数调用时生效,在调用结束后解除。
形参和实参的使用
位置参数
位置即顺序,位置参数指的是按顺序定义的参数,需要从两个角度去看:
在定义函数时,按照从左到右的顺序依次定义形参,称为位置形参,凡是按照这种形式定义的形参都必须被传值
## 有位置而不传值会产生错误
def register(name,age,sex): #定义位置形参:name,age,sex,三者都必须被传值
print('Name:%s Age:%s Sex:%s' %(name,age,sex))
register() #TypeError:缺少3个位置参数
## TypeError: register() missing 3 required positional arguments: 'name', 'age', and 'sex'
'''
## 不按顺序传值会导致值的对应错误
def register(name, age, sex): # 定义位置形参:name,age,sex,三者都必须被传值
print('Name:%s Age:%s Sex:%s' % (name, age, sex))
register(18, "男", "Xiao")
# Name:18 Age:男 Sex:Xiao
'''
## 正确的位置传参
def register(name, age, sex): # 定义位置形参:name,age,sex,三者都必须被传值
print('Name:%s Age:%s Sex:%s' % (name, age, sex))
register("Xiao", 18, "男")
# Name:Xiao Age:18 Sex:男
在调用函数时,按照从左到右的顺序依次定义实参,称为位置实参,凡是按照这种形式定义的实参会按照从左到右的顺序与形参一一对应
关键字参数
在调用函数时,实参可以是key=value的形式,称为关键字参数,凡是按照这种形式定义的实参,可以完全不按照从左到右的顺序定义,但仍能为指定的形参赋值
register(sex='male',name='lili',age=18)
Name:lili Age:18 Sex:male
位置和关键字混合使用
需要注意在调用函数时,实参也可以是按位置或按关键字的混合使用,但必须保证关键字参数在位置参数后面,且不可以对一个形参重复赋值
register('lili',sex='male',age=18) #正确使用
register(name='lili',18,sex='male') #SyntaxError:关键字参数name=‘lili’在位置参数18之前
register('lili',sex='male',age=18,name='jack') #TypeError:形参name被重复赋值
默认参数
在定义函数时,就已经为形参赋值,这类形参称之为默认参数,当函数有多个参数时,需要将值经常改变的参数定义成位置参数,而将值改变较少的参数定义成默认参数。例如编写一个注册学生信息的函数,如果大多数学生的性别都为男,那完全可以将形参sex定义成默认参数
- 默认参数必须在位置参数之后
- 默认参数的值仅在函数定义阶段被赋值一次
def register(name,age,sex='male'): #默认sex的值为male
print('Name:%s Age:%s Sex:%s' %(name,age,sex))
默认参数的值通常应设为不可变类型
def foo(n,arg=[]):
arg.append(n)
return arg
foo(1)
[1]
foo(2)
[1, 2]
foo(3)
[1, 2, 3]
定义时就已经为参数sex赋值,意味着调用时可以不对sex赋值,这降低了函数调用的复杂度
register('tom',17) #大多数情况,无需为sex传值,默认为male
Name:tom Age:17 Sex:male
register('Lili',18,'female') #少数情况,可以为sex传值female
Name:Lili Age:18 Sex:female
小结:
为了防止有些参数重复或者报错 -- 默认参数
## 默认参数如果不想填,None
def index(name,age,gender='男'):
print(f'{name}-{age}-{gender}')
## 默认参数不传参数的时候就使用默认值
index('dream',18)
## 默认参数传了参数就会覆盖掉默认值
index('hope',18,'女')
无论是默认参数的位置还是关键字参数的位置,一定要在位置参数后面
可变长参数
参数的长度可变指的是在调用函数时,实参的个数可以不固定,而在调用函数时,实参的定义无非是按位置或者按关键字两种形式,这就要求形参提供两种解决方案来分别处理两种形式的可变长度的参数。
可变长位置参数(*args)
如果在最后一个形参名前加*
号,那么在调用函数时,溢出的位置实参,都会被*
接收,以元组的形式保存下来赋值给该形参
def foo(x,y,z=1,*args): #在最后一个形参名args前加*号
print(x)
print(y)
print(z)
print(args)
foo(1,2,3,4,5,6,7) #实参1、2、3按位置为形参x、y、z赋值,多余的位置实参4、5、6、7都被*接收,以元组的形式保存下来,赋值给args,即args=(4, 5, 6,7)
1
2
3
(4, 5, 6, 7)
如果我们事先生成了一个列表,仍然是可以传值给*args的
def foo(x,y,*args):
print(x)
print(y)
print(args)
L=[3,4,5]
foo(1,2,*L) # *L就相当于位置参数3,4,5(解包), foo(1,2,*L)就等同于foo(1,2,3,4,5)
1
2
(3, 4, 5)
注意:如果在传入L时没有加*,那L就只是一个普通的位置参数了
def foo(x, y, *args):
print(x)
print(y)
print(args)
L = [3, 4, 5]
foo(1, 2, L)
# 1
# 2
# ([3, 4, 5],)
# 如果在传入 L 时没有加 `*` ,那 L 就只是一个普通的位置参数了
# ([3, 4, 5],) ----> 为什么有一个 , ? 以元组的形式保存下来赋值给原来的参数
如果形参为常规的参数(位置或默认),实参仍可以是*的形式
def foo(x,y,z=3):
print(x)
print(y)
print(z)
foo(*[1,2]) #等同于foo(1,2)
1
2
3
如果我们想要求多个值的和,*args
就派上用场了
def index(*args):
start = 0
for i in range(len(args)):
start += args[i]
print(start)
## 1 + 2 + 3
index(1, 2, 3) # 6
可变长关键词参数(*kwargs)
- 如果在最后一个形参名前加
**
号,那么在调用函数时,溢出的关键字参数,都会被**
接收,以字典的形式保存下来赋值给该形参 - 可变长关键字参数 : 传实参的时候要按照关键字去传
def foo(x, **kwargs): # 在最后一个参数kwargs前加**
print(x)
print(kwargs)
foo(y=2, x=1, z=3)
# 溢出的关键字实参y=2,z=3都被**接收,以字典的形式保存下来,赋值给kwargs
# 1
# {'y': 2, 'z': 3}
- 如果我们事先生成了一个字典,仍然是可以传值给
**kwargs
的
def foo(x, y, **kwargs):
print(x)
print(y)
print(kwargs)
dic = {'a': 1, 'b': 2}
foo(1, 2, **dic)
# **dic就相当于关键字参数a=1,b=2,foo(1,2,**dic)
# 等同foo(1,2,a=1,b=2)
# 1
# 2
# {'a': 1, 'b': 2}
- 如果在传入 dic 时没有加
**
,那 dic 就只是一个普通的位置参数了
def foo(x, y, **kwargs):
print(x)
print(y)
print(kwargs)
dic = {'a': 1, 'b': 2}
foo(1, 2, dic)
'''
Traceback (most recent call last):
File "E:\PythonProjects\01.py", line 358, in <module>
foo(1, 2, dic)
TypeError: foo() takes 2 positional arguments but 3 were given
'''
- 如果形参为常规参数(位置或默认),实参仍可以是
**
的形式
def foo(x, y, z=3):
print(x)
print(y)
print(z)
foo(**{'x': 1, 'y': 2}) # 等同于foo(y=2,x=1)
组合使用
def register(name, age, *args, **kwargs):
user_dict = {'name': name, 'age': age}
# 如果有位置参数进来而没有变量名接收的时候就会打包成元组,放到上面的字典中
if args:
user_dict['additional_info'] = args
# 如果有关键字参数进来,就会被字典更新
if kwargs:
user_dict.update(kwargs)
return user_dict
user_dict = register("Xiao", 18, "handsome",'music', hobby=["music"])
print(user_dict) # {'name': 'Xiao', 'age': 18, 'additional_info': ('handsome', 'music'), 'hobby': ['music']}
命名关键字参数
在函数定义形参时,如果出现 * 隔断两边的形参,前面的参数任意(可位置/可关键字), * 后面的参数必须按照关键字传参数
def login(name,pwd,*,hobby,sex):
print(f'{name}-{pwd}-{hobby}-{sex}')
login('dream', '521', hobby='music', sex='男')
# 命名关键字参数也可以使用默认值
# 默认关键字参数的位置可以放在位置参数前面
def login(name, pwd, *, hobby='music', sex):
print(f'{name}-{pwd}-{hobby}-{sex}')
login('xiao', '000', sex='男')
login('xiao', '000', hobby='swim', sex='男')
- * 号后面的参数必须用关键字命名,* 前面任意使用
- 需要在定义形参时,用 * 作为一个分隔符号,* 号之后的形参称为命名关键字参数。
- 对于这类参数,在函数调用时,必须按照 key=value 的形式为其传值,且必须被传值
以上所有参数混合使用
- 综上所述所有参数可任意组合使用,但定义顺序必须是:
位置参数
、默认参数
、*args
、命名关键字参数
、**kwargs
- 可变参数
*args
与关键字参数**kwargs
通常是组合在一起使用的 - 如果一个函数的形参为
**args
与**kwargs
,那么代表该函数可以接收任何形式、任意长度的参数
def wrapper(*args,**kwargs):
pass
- 在该函数内部还可以把接收到的参数传给另外一个函数(这在装饰器的实现中大有用处)
def func(x, y, z):
print(x, y, z)
def wrapper(*args, **kwargs):
func(*args, **kwargs)
wrapper(1, z=3, y=2)
# 1 2 3
- 提示:
*args
、**kwargs
中的 args 和 kwargs 被替换成其他名字并无语法错误,但使用args、kwargs是约定俗成的。