day14:02函数各参数总结与可变长参数

一、位置形参与默认形参混用(***)

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)

三、总结:

① 函数分为两大类,分别是什么?二者在使用时有何区别?

  1. 函数的两大类:内置函数与自定义函数

② 形参与实参的关系

  1. 形参是在函数定义阶段传入的参数(相当于变量名),实参是在函数调用阶段传入的值
  2. 在调用阶段,实参(变量值)会绑定给形参(变量名)
  3. 这种绑定关系只能在函数体内使用
  4. 实参与形参的绑定关系在函数调用时生效,函数调用结束后解除绑定关系

③ 简述两形参的区别:位置形参与默认形参

  1. 定义:
    位置形参:在函数定义时,按照从左到右的顺序直接定义的变量名
    默认形参:为某些形参指定默认值
  2. 位置:默认形参必须在位置形参的后面
  3. 调用:位置形参必须有一一对应的值,默认形参有默认值,调用函数时可以不用单独传值

④ 简述两实参的区别:位置实参与关键字实参

<共同点>:都是在函数调用阶段

<区别>:

  1. 定义:
    位置实参:在函数调用阶段, 按照从左到有的顺序依次传入的“值”
    关键字实参: 在函数调用阶段,按照key=value的形式传入的值
  2. 实参传递顺序要求:
    位置实参:必须严格按照函数定义中形参的顺序进行传递
    关键字实参: 传递顺序与函数定义中形参的顺序无关
  3. 位置实参不指定参数名, 关键字实参需指定参数名

⑤ 简述 * 与 ‘**’ 在形参与实参中的区别

’ * ’ ==> 用来接收溢出的位置实参,溢出的部分保存为元组并赋予给紧随其后的形参,形参约定使用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的参数特点是什么

  1. wrapper的参数规则必须按照index的参数规则来
  2. wrapper传入的参数,先被wrapper打包处理,即args = (1,) kwargs = {‘z’:3, ‘y’:2}
  3. index接收打包的数据,接收的是实参,即index( *(1,), ** {‘z’:3, ‘y’:2})。我们知道,( * ) 和( ** ) 在形参中接收数据是汇总,而在实参中( * ) 和( ** )接收的数据是打散,( * )后面的元组打散成位置实参,( ** )后面的关键字实参打散成字典,所以 index( *(1,), ** {‘z’:3, ‘y’:2})等价于index(1, z=3, y=2)
  4. 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)
  • 28
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值