参数

形参顺序:位置参数(name) ->默认参数(name=value) -> *defs /单个*-> name或name = value,keyword-only -> **defs

实参顺序:字面值                 -> 关键字形参/*args 它们之间不分前后   -> **args             

     

形参列表中位置形参必须在默认形参前面的原因?即def fun( a, b=4, c)是错误的。
根本原因是形参中(name=value)形式只是给一个变量一个默认值而已,它仍可以通过位置参数进行赋值;假设现在有def fun(a,c=5,b)现在如果我不想给c赋值了,那我在实参中如何定义呢?你如何从c跳到b呢?因为c仅仅是有默认值而已,从左到右依次赋值的话,肯定是先c后b.你有可能说那就将c也整成keyword-only,感觉应该可以跳过c了把,但是如果想是keyword-only,c前面必须是*name或单个*,然后b也自然成为了keyword-only.所以说在形参列表中默认形参后面不能有普通的位置参数。


实参列表中为何形式fun(4, b=5,6)也是错误的呢?

参考下面描述实参与形参对应的顺序,因为一旦碰到关键字形参的话,第一步的位置参数匹配就结束了,也就是6没有办法再进行对应了,所以像上面那么书写。(这个是个人猜测)


默认值参数和关键字参数

name=value在形参列表中代表默认值参数,而在实参列表中代表关键字参数

在实参列表中它意味这通过变量名进行匹配的关键字,而在形参列表中它为可选参数定义默认值。但是无论哪种情况,这都不是一个赋值语句。它只是改变了参数匹配机制。


keyword-only

keyword-only 只是针对形参中的变量而言的,因为我们在实参中可以对变量都按关键字进行赋值,不管你在形参中是位置参数或者keyword-only。但是反之,如果在形参中是keyword-only的话,实参中不指明该变量名的话,就会报错。

两种方式限定形参变量为keyword-only

1:前面有个单独的*,因为*name不存在的情况下,我们就需要引用这中形式指示一些变量为keyword-only形式

2:*name后面的变量,它既可以限定后面形参变量为keyword-only,同时可以接受来自实参中多余的非key=value对象。

在一个式子中他们两个不会同时出现,否则会报'syntax error'

>>> def test(a,b,*,c,*d):
    def test(a,b,*,c,*d):
                     ^
SyntaxError: invalid syntax

>>> def test(a,b,*c,*,d):
    def test(a,b,*c,*,d):
                    ^
SyntaxError: invalid syntax

*name后面变量必须按关键字赋值,否则报错

>>> def tet(a,b,*c,d,e=7,**f):  
...     print(a,b,c,d,e,f)  
...   
>>> tet(1,2,3,d=5,x=9)  
1 2 (3,) 5 7 {'x': 9}  
>>> tet(1,2,3,4,x=9)                           #1,2根据位置赋值给a,b,然后3,4给*c,最后d悲剧了  
TypeError: tet() needs keyword-only argument d #说明你只能按关键字给他赋值 

在实参列表中的约束条件

keyword-only参数可以在*args 之前或之后,并且可以包含在**args 中,但是决不能在**args之后

>>> def f(a,*b,c=6,**d):                 #根据前面的结论,c是一个keyword-only
... 	print(a,b,c,d)
... 
>>> dict(x=5,y=6)                        #dict方法把关键字组合转化成字典,再通过实参中**将字典解包
{'y': 6, 'x': 5}
>>> f(1,*(2,3),**dict(x=4,y=5),c=7)      #实参列表中c不能在**之后
SyntaxError: invalid syntax
>>> f(1,*(2,3),c=7,**dict(x=4,y=5))      #实参列表中c在*args与**args之间
1 (2, 3) 7 {'y': 5, 'x': 4}
>>> f(1,c=7,*(2,3),**dict(x=4,y=5))
1 (2, 3) 7 {'y': 5, 'x': 4}
>>> f(1,*(2,3),**dict(x=4,y=5,c=7))      #实参列表中c在**args中
1 (2, 3) 7 {'y': 5, 'x': 4}
keyword-only用途:它主要用于一些可能的配置选项,可以不给值(前提是给了默认值),但是给值的话,必须按关键字形式。


* 与** 的语法对比

*defs 一个参数名称出现在它之前,它可能时默认位置参数,而不是keyword-only 参数

*args 形式是一个迭代环境,因为它每次解包出来一个,然后再进行相当next的操作来处理下一条,因此技术上它接受任何可迭代对象,而不仅仅是元组和序列,比如文件对象func(*open('fname'))

在形参列表中,它收集任意数量的参数,*收集额外的位置参数到元组,从实参中的来源 剩余的普通实参,*arg中剩余的值;**将关键字参数转换为字典,它的来源是实参中剩余的name=value(其中含有由**arg 中为匹配的键/值对)。

在实参列表中,*用于对应形参列表中未匹配到的非keyword-only变量,有剩余的话,添加到*def的元组。**先把字典中的key:value形式,转化为key=value的关键字形式,然后再对应形参列表中未匹配到的非keyword-only和keyword-only变量,也就是形参中除(*defs,**defs)的所有未匹配的变量,如果最后形参按照关键字都对应完了,还有剩余的key=value,那么就把value加入到**def的元组中。

>>> def f(a,*b,c=6,**d):                 #根据前面的结论,c是一个keyword-only
... 	print(a,b,c,d)
... 
>>> dict(x=5,y=6)                        #dict方法把关键字组合转化成字典,再通过实参中**将字典解包
{'y': 6, 'x': 5}
>>> f(1,*(2,3),c=7,**dict(x=4,y=5))      #词典先转换成x=4,y=5关键字形式,然后跟剩下的参数按照关键字匹配,最终剩下的x=4,y=5关键字参数赋给**d
1 (2, 3) 7 {'y': 5, 'x': 4}

解包就是减少一层括号约束,凡是遇到*符号,潜意识知道这个要解包,并且将要传值给实参

解包应用注意事项:

1:在实参中,我们会把列表,元组,string,等都看成一个整体,除非前面有*/**解包

>>> def test(*seq):
... 	print(seq)              #seq是一个元组
... 
>>> test((1,2,3),(5,6))
((1, 2, 3), (5, 6))
>>> def test1(*seq):
... 	print(*seq)            #因为对seq解包了,与原来相比就相当与少了一层括号。
>>> test1((1,2,3),(6,7))
(1, 2, 3) (6, 7)
2:当实参列表最后只剩下一个单元赋予*args 的时候,args 的值实际上了新加一个外层括号的同时,内侧还有一个逗号。即如果4赋给*args 的话,args=(4,) 因为它是args必须是一个元组,它表示只有一个元数4的元组。同时在你解包的时候,只要列表,或元组前没有*/** 它就仍旧作为一个整体

>>> def test(*args):
... 	print(args)
... 
>>> test(1)               
(1,)
>>> test((1,2))           #元组作为一个整体赋给args
((1, 2),)
>>> test(((1,2),(3,4)))   #元组作为一个整体赋给args
(((1, 2), (3, 4)),)
>>> (4)                   #单纯的在4外层加括号,它的值仍旧是4,所以另加一个,成为元组
4
>>> ((1,2))               #单纯的加一个括号以后它的值不变
(1, 2)
>>> ((1,2),)              	
((1, 2),)
3:因为你既然在赋值给形参的时候,都会添加一个额外的括号,作为一层的扩展,所以说我们经常在函数定义的时候,需要用*defs 的形式来解包。它实际上就是把加的括号去掉。 详细可以参见

实参与形参如何对应赋值

因为我们实参的话,就是用于对形参中参数的匹配和赋值,我下面的描述是那实参列表去对应形参列表:

形参中的变量分为:位置形参,keyword-only参数,*defs,**defs     (默认参数可以按照位置赋值,所以也划为位置形参)

实参中的变量分为:字面值,关键字参数,*args,**args
1:   首先实参列表中的普通数据跟形参列表对应,如果实参中位置参数有剩余就加入到*defs 中。
2:如果碰到关键字参数,步骤1结束;实参列表中的关键字参数对应到形参列表中的参数,如果有一些未找到对应项,就存入到**defs的字典中。
3:当所有的关键字参数匹配结束,步骤2结束;在形参列表中没有匹配上的非关键字参数与*args 中的值进行位置匹配,如果在*args 中有剩余的话,就把它放入到*name中
4:当与*args 中值匹配完成,步骤3结束;首先**后的字典key:value转换为关键字参数key-value,然后形参列表中仍旧未匹配到的非关键字形参,或keyword-only参数到**args 里面按关键值进行匹配;如果最后有关键字参数剩余的话,就把关键字参数放入到**name词典中。

5:用默认值分配给形参中仍旧没有匹配到的参数

6:Python检验是否每个参数都有唯一的值,否则报错。匹配成功的话,就把实参中的对象进行相应的赋值。

>>> def test(a,b,c,*d,e,f,**g):
... 	print(a,b,c,d,e,f,g)
... 
>>> test(1,2,e=4,*(7,8),x=5,**{'f':2,'y':6})
1 2 7 (8,) 4 2 {'y': 6, 'x': 5}

首先1,2与a,b按照位置对应,然后碰到关键字形参e步骤1结束,形参e匹配到4;然后发现x=5是多余的,所以就添加到**g字典中。然后开始把剩下的非关键字形参c跟*(7,8)进行位置匹配,c对应个7,剩下了一个8,然后8就被存在了*d的元组中,然后就该说**的事情了,首先转化为f=2,y=6形式,然后剩下的keyword-only f根据关键字对应到2,最后剩下了一个y=6,然后把它也加入到**g字典中。所以最后**g值就是{'y': 6, 'x': 5}


总结:

1:其实解包就是对一大单元分解成一个个小单元的过程,另一方面也可以认为就是去掉一层括号之类的,比如((1,2,3),(4,5,6))解包的话,将是(1,2,3),(4,5,6)。'spam' 将是's','p','a','m'

2:其实for循环实际上也就是先把一个大的单元分解成一小块,然后进行处理。而对于如何分成小块这个就是后面对象next()方法是如何实现的。对于内置对象比如string跟上面一样分成's','p','a','m' 赋值给i,然后接着处理for中的逻辑体。


【本章*/**在形参列表用*defs / **defs, 实参列表用 *args / **args】
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值