目录
一、位置形参与默认形参混用(***)
1、位置形参必须在默认形参的左边
def func(y=2,x):
pass # SyntaxError: invalid syntax
2、默认参数的值是在函数定义阶段被赋值的,准确地说被赋予的值是值的内存地址
'''示范1:'''
m=2
def func(x,y=m): # y=>2的内存地址,即将2的内存地址传给了y,即y指向2这个值对应的内存空间
print(x,y)
m=3333333333333333333
func(1) # 结果:1 2
'''示范2:'''
m = [111111, ]
def func(x, y=m): # y=>[111111, ]的内存地址
print(x, y)
m.append(3333333)
func(1) # 结果:1 [111111, 3333333]
3、虽然默认值可以被指定为任意数据类型,但是不推荐使用可变类型
函数最理想的状态:函数的调用只跟函数本身有关系,不外界代码的影响
'''案例'''
m = [111111, ]
def func(x, y=m):
print(x, y)
m.append(3333333)
m.append(444444)
m.append(5555)
func(1) # 1 [111111, 3333333, 444444, 5555]
func(2) # 结果: 2 [111111, 3333333, 444444, 5555]
func(3) # 结果: 3 [111111, 3333333, 444444, 5555]
4、默认形参中定义一个空列表,或者定义一个变量且通过另外一个变量进行传递,当代码过大时会存在被篡改的风险,所以使用下面的方法
'''案例:不太好的写法,见上述 示范2 ,其实可以,但是非必要'''
'''******正确案例******'''
def func(x,y,z,l=None):
if l is None:
l=[]
l.append(x)
l.append(y)
l.append(z)
print(l)
#
# func(1,2,3) # 结果:[1, 2, 3]
# func(4,5,6) # 结果:[4, 5, 6]
new_l=[111,222]
func(1,2,3,new_l) # 结果:[111, 222, 1, 2, 3]
二、可变长度的参数(*与**的用法)
可变长度指的是在调用函数时,传入的值(实参)的个数不固定,而实参是用来为形参赋值的,所以对应着,针对溢出的实参必须有对应的形参来接收
2.1 可变长度的位置参数
【number1】形参名:用来接收溢出的位置实参,溢出的位置实参会被 * 保存成元组的格式 然后赋值紧跟其后的形参名, * 后跟的可以是任意名字,但是约定俗成应该是args
'''案例1:位置形参与可变长度的位置参数'''
def func(x,y,*z): # z =(3,4,5,6)
print(x,y,z)
func(1,2,3,4,5,6) # 1 2 (3, 4, 5, 6)
'''案例2:定义一个加法功能,可接受任意实参'''
def my_sum(*args):
res=0
for item in args:
res+=item
return res
res=my_sum(1,2,3,4)
print(res)
【number2】 可以用在实参中,实参中带*,先*后的值打散成位置实参
'''案例3:*在实参中的应用'''
def func(x,y,z):
print(x,y,z)
func(*[11,22,33]) # 先打散,相当于func(11,22,33) ,结果:11 22 33
# func(*[11,22]) # func(11,22)
l=[11,22,33]
func(*l) # 先打散,相当于func(11,22,33),结果:11 22 33
【number3】 形参与实参中都带*
'''案例4:*后的实参为列表'''
def func(x,y,*args): # args=(3,4,5,6)
print(x,y,args)
func(1,2,[3,4,5,6]) # 1,2,3,4,5,6
func(1,2,*[3,4,5,6]) # 先打散,func(1,2,3,4,5,6),结果:1,2,3,4,5,6
func(*'hello') # 先打散,func('h','e','l','l','o'),结果:h e ('l', 'l', 'o')
# 注:元组中的字符(串),需用''引起来
'''案例5:*后的实参为字典,打散后的值为字典的key值'''
def func(x,y,*args): # args=(3,4,5,6)
print(x,y,args)
func(*{'a':111, 'b':222, 'c':333})
# 先打散,func(a, b, c),x,y分别接受a和b,溢出的位置实参会被 *,
# 故args接收‘c’时被保存为元组,即为('c',)
# 故结果:a b ('c',)
2.2 可变长度的关键字参数
【number1】形参名:用来接收溢出的关键字实参,** 会将溢出的关键字实参保存成字典格式,然后赋值给紧跟其后的形参名, **后跟的可以是任意名字,但是约定俗成应该是kwargs
'''案例1:可变长度的关键字参数**kwargs'''
def func(x,y,**kwargs):
print(x,y,kwargs)
func(1,y=2,a=1,b=2,c=3) # 结果:1 2 {'a': 1, 'b': 2, 'c': 3}
【number2】* 可以用在实参中( * 后跟的只能是字典),实参中带 * *,先将**后的值打散成关键字实参
'''案例2:**后跟字典'''
def func(x,y,z):
print(x,y,z)
func(*{'x':1,'y':2,'z':3}) # 先打散,func('x','y','z'),结果:x y z
func(**{'x':1,'y':2,'z':3}) # 先打散,func(x=1,y=2,z=3),结果:1 2 3
'''错误案例:缺少位置参数、关键字参数不对'''
def func(x,y,z):
print(x,y,z)
func(**{'x':1,'y':2,}) # func(x=1,y=2)
# TypeError: func() missing 1 required positional argument: 'z'
func(**{'x':1,'a':2,'z':3}) # func(x=1,a=2,z=3)
# TypeError: func() got an unexpected keyword argument 'a'
【number3】形参与实参中都带
'''案例3:'''
def func(x,y,**kwargs):
print(x,y,kwargs)
func(y=222,x=111,a=333,b=444) # 结果: 111 222 {'a'::333, 'b':444}
func(**{'y':222,'x':111,'a':333,'b':4444})
# 先打散,func(y=222, x=111, a=333, b=444),结果:111 2222 {'a':333, 'b':4444}
2.3 混用 * 与 ** , *args必须在 **kwargs之前
'''案例1:*接收大量位置实参、**接受关键字'''
def func(x,*args,**kwargs):
print(x) # 结果:1
print(args) # 结果:(2, 3, 4, 5, 6, 7, 8)
print(kwargs) # 结果:{'w':1, 'y':2, 'z':3}
func(1,2,3,4,5,6,7,8,w=1,y=2,z=3) #见上面
func(1,2,3,4,5,6,7,8,x=111,y=2,z=3) # TypeError: func() got multiple values for argument 'x'
# 分析:根据位置对应,1传给x了,由于后面又定义了x=111,重复传值报错
2.4 *args与**kwargs嵌套使用(重点)
'''案例1:参数转发案例'''
def index(x,y,z):
print('index=>>> ',x,y,z)
def wrapper(*args,**kwargs): # args=(1,) kwargs={'z':3,'y':2}
index(*args,**kwargs) # 解包,打散,即 *args= *(1,) = 1
# **kwargs=**{'z':3,'y':2} ==> z=3, y=2
# 故 index(*args,**kwargs) ==>> index(1,z=3,y=2)
# index(*(1,),**{'z':3,'y':2})
# index(1,z=3,y=2)
wrapper(1,z=3,y=2)
三、总结:
① 函数分为两大类,分别是什么?二者在使用时有何区别?
- 函数的两大类:内置函数与自定义函数
② 形参与实参的关系
- 形参是在函数定义阶段传入的参数(相当于变量名),实参是在函数调用阶段传入的值
- 在调用阶段,实参(变量值)会绑定给形参(变量名)
- 这种绑定关系只能在函数体内使用
- 实参与形参的绑定关系在函数调用时生效,函数调用结束后解除绑定关系
③ 简述两形参的区别:位置形参与默认形参
- 定义:
位置形参:在函数定义时,按照从左到右的顺序直接定义的变量名
默认形参:为某些形参指定默认值- 位置:默认形参必须在位置形参的后面
- 调用:位置形参必须有一一对应的值,默认形参有默认值,调用函数时可以不用单独传值
④ 简述两实参的区别:位置实参与关键字实参
<共同点>:都是在函数调用阶段
<区别>:
- 定义:
位置实参:在函数调用阶段, 按照从左到有的顺序依次传入的“值”
关键字实参: 在函数调用阶段,按照key=value的形式传入的值- 实参传递顺序要求:
位置实参:必须严格按照函数定义中形参的顺序进行传递
关键字实参: 传递顺序与函数定义中形参的顺序无关- 位置实参不指定参数名, 关键字实参需指定参数名
⑤ 简述 * 与 ‘**’ 在形参与实参中的区别
’ * ’ ==> 用来接收溢出的位置实参,溢出的部分保存为元组并赋予给紧随其后的形参,形参约定使用args;
’ ** ’ ==> 用来接收溢出的关键字实参,溢出的部分保存为字典并赋予给紧随其后的形参,形参约定使用kwargs;
使用区别:使用下方案例说明
'''* 后可接:多个位置实参、列表、元组、字典'''
def func(x, y, *args):
''' 打印值'''
print(x, y, args)
def func111(x, y, *args):
'''解包后打印值'''
print(x, y, *args)
'''多个位置实参:溢出部分保存为元组'''
func(11, 22, 33) # 结果:11 22 (33,)
func111(11, 22, 33) # 结果:11 22 33
# 注: z 指向的是(33,)的内存地址,*z解包后值为33
'''列表:'''
func(*[11, 22, 33, 44]) # 打散==> 结果:11 22 (33, 44)
func111(*[11, 22, 33, 44]) # 结果:11 22 33 44
# 注: z 指向的是 (33, 44)的内存地址,*z解包后值为 (33, 44)
'''字典:'''
func(*{'aa':11, 'bb':22, 'cc':33}) # 结果:aa bb (cc,)
func111(*{'aa':11, 'bb':22, 'cc':33}) # 结果:aa bb cc
'''元组:'''
func(*(11, 22, 33)) # 结果:11 22 (33,)
func111(*(11, 22, 33)) # 结果:11 22 33
''' **后接关键字实参'''
def func(x, y, **kwargs):
''' 打印值'''
print(x, y, kwargs)
def func111(x, y, **kwargs):
'''解包后打印值'''
print(x, y, **kwargs)
'''关键字实参:**会将溢出的关键字实参保存为字典'''
func(11, 22, z=33) # 11 22 {'z': 33}
# func111(11, 22, z=33) #TypeError: 'z' is an invalid keyword argument for print()
# 失败原因:print 函数并不直接支持使用 **kwargs 语法来展开字典参数,因为它期望的是一系列单独的参数,而不是一个字典。
func(**{'x':11, 'y':22}) # 结果:11 22 {}
func(**{'x':11, 'y':22, 'cc':33}) # 结果:11 22 {'cc': 33}
⑥ 解释函数wrapper的参数特点是什么
- wrapper的参数规则必须按照index的参数规则来
- wrapper传入的参数,先被wrapper打包处理,即args = (1,) kwargs = {‘z’:3, ‘y’:2}
- index接收打包的数据,接收的是实参,即index( *(1,), ** {‘z’:3, ‘y’:2})。我们知道,( * ) 和( ** ) 在形参中接收数据是汇总,而在实参中( * ) 和( ** )接收的数据是打散,( * )后面的元组打散成位置实参,( ** )后面的关键字实参打散成字典,所以 index( *(1,), ** {‘z’:3, ‘y’:2})等价于index(1, z=3, y=2)
- wrapper(1,z=3,y=2) 的结果为:index=>>> 1 2 3
'''再啰嗦一下'''
def index(x,y,z):
print('index=>>> ',x,y,z)
def wrapper(*args,**kwargs):
index(*args,**kwargs)
wrapper(1,z=3,y=2)