參數

形參順序:位置參數(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】
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章