【Python學習】python學習手冊--第十八章 參數

傳遞參數

  • 參數的傳遞是通過將對象賦值給函數本地變量名來實現的。傳遞就是賦值,函數本地變量名是對傳遞進來的對象的引用。所以默認情況下,被傳遞的對象從來不會自動拷貝
  • 在函數內部的參數名的賦值不會影響調用者。函數頭部的參數,是函數本地作用域內的變量名,與調用者作用域內的變量名不會重疊。
  • 改變函數的可變對象參數的值也許會對調用者有影響。
  • 不可變參數“通過值”進行傳遞。像整數和字符串這類對象是通過對象引用而不是拷貝來進行傳遞的,當然,這類對象都是不可變的類型,在函數的作用域內的效果就像是創建了一份拷貝。
  • 可變對象是通過“指針”進行傳遞。可變對象通過引用傳遞給函數參數,意味着在函數內部對可變對象的修改就可能影響到該對象在調用者中的使用。
>>> def change(x):
...     x=33                                 
...     print(x," in the change function")
... 
>>> x=22
>>> change(x)               #在函數內部的賦值操作不會改變調用者中的變量
33  in the change function
>>> x
22
>>> 
>>> def change(x):
...     x.append(33)
...     print(x," in the change function")
... 
>>> x=[1,2,3,4,5]
>>> change(x)
[1, 2, 3, 4, 5, 33]  in the change function
>>> x                   #可變對象能在原處修改,在函數內部的修改相對於在對象本身做修改
[1, 2, 3, 4, 5, 33]
>>> 

這裏如果理解了變量名與對象之間的關係,你就能更清楚的理解參數的傳遞。可以查看之前的博文

避免改變可變參數

爲了避免可變參數在函數內部被修改,你可以傳遞對象的新拷貝進入函數,而不是默認賦值情況下,傳遞對象的引用進入函數。

>>> def change(x):
...     x.append(33)
...     print(x," in the change function")
... 
>>> x=[1,2,3,4,5]
>>> x
[1, 2, 3, 4, 5]
>>> change(x[:])        #傳遞列表的拷貝,如果是其它可變類型,可以使用copy.copy或copy.deepcopy函數
[1, 2, 3, 4, 5, 33]  in the change function
>>> x
[1, 2, 3, 4, 5]
>>> 

參數匹配模型

傳遞的參數可以是多個,在有多個參數傳遞的時候,就存在參數匹配函數本地變量的模型,模型是根據函數的變量名來選擇的,選擇完成之後,對函數本地變量名的傳遞本質還是賦值。
參數匹配模型有以下幾種方式:

  • 位置:從左至右進行匹配
  • 關鍵字參數:通過參數名進行匹配,傳遞變量時用name=value
  • 默認參數:在沒有參數傳入時定義的默認值,形式通常在定義函數時使用name=value
  • 可變參數:收集任意多基於位置或關鍵字的參數(形式就像*arg用於收集列表 或**arg用於收集字典),一般在函數的定義時出現。
  • 可變參數解包:傳遞任意多的基於位置或關鍵字的參數(形式就像*arg用於傳遞列表 或**arg用於傳遞字典)。在函數的調用時出現。
  • keyword-only參數:必須按照關鍵字傳遞的參數

在函數定義時,參數匹配模型的語法:

語法 解釋
def func(name1,name2,name3) 最常見的匹配模式,通過從左至右的位置順序進行匹配
def func(name1=value1,…name3=value3) 默認參數值,如果沒有在調用中傳遞值的話
def func(*name) 匹配並收集元組中所有包含位置的參數
def func(**name) 匹配並收集字典中所有包含關鍵字的參數
def func(*args,name) 參數必須在調用中按照關鍵字傳遞
def func(*, name=value) Python3.0中引進,任何默認的或正式的參數都是keyword-only參數,必須在調用時按照關鍵字傳遞。

在函數調用時,也有相關的參數匹配模式:

語法 解釋
func(value1,value2,value3) 最常見的匹配模式,按照從左到右的位置將參數值匹配進函數本地變量
func(name1=value1,name2=value2,name3=value3) 通過關鍵字匹配,表現形式更直觀,將函數的參數賦值
func(*sequence) 以sequence列表傳遞基於從左至右位置順序的參數。
func(**dict) 字典中的鍵爲函數的參數變量名,在字典中傳遞相應的值給函數變量名

在函數定義的頭部,

  • 一個簡單的參數名是通過位置或變量名進行匹配的(可以用位置傳遞,也可以用關鍵字傳遞)
  • 頭部出現name=value,指定了參數名的默認值
  • *name收集了任意的額外不匹配的參數到元組中
  • **name的形式將會收集額外的關鍵字參數到字典中

其中關鍵字參數和默認參數在Python中是比較常見的使用方式。
如果要決定並混合特定的參數匹配模型,Python將會遵循下面有關順序的法則:

  • 在調用函數時,參數必須以此先後順序出現:任何位置參數(value),任何關鍵字參數(name=value)和*sequence形式的組合,最後是**dict形式
  • 在函數頭部,參數必須以此順序出現:任何一般參數(name),默認參數(name=value),*name的形式,跟一般參數或關鍵字參數,最後跟**name形式的參數

不管是調用或者定義函數的時候,**arg形式就必須出現在最後。
在Python內部是使用以下的步驟來在賦值前進行參數匹配的:

  • 通過位置分配非關鍵字參數
  • 通過匹配變量名分配關鍵字參數
  • 其它額外的非關鍵字參數分配到*name元組中
  • 其它額外的關鍵字參數分配到**name字典中
  • 用默認值分配給函數頭部沒有獲得值的變量

參數調用實例

>>> def func(a,b,c): print(a,b,c)
... 
>>> func(1,2,3)               #按照位置順序,依次賦值參數名
1 2 3
>>> func(a=3,c=2,b=1)         #也可以通過關鍵字的形式,給函數參數賦值
3 1 2
>>> func(1,b=2,c=3)          #混用的情況
1 2 3
>>> func(a=1,2,3)            #匹配模式混用時要注意按照上文所說的出現順序來使用。
  File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument
>>> 

任意參數的實例

可以用*arg的形式,接受傳遞進來的任意值,並把它們組成元組:

>>> def func(*arg):print(arg)        
... 
>>> func()
()
>>> func(1)
(1,)                #得到的是元組對象
>>> func(1,2,3,4,5)
(1, 2, 3, 4, 5)
>>> 

而**arg也有相同的效果,但是他們得到的是字典對象:

>>> def func(**arg):print(arg)
... 
>>> func()
{}
>>> func(a=1)
{'a': 1}
>>> func(a=1,b=2,c=3,d=4)          #傳入的形式是關鍵字的形式傳遞進入函數中,函數中得到的是字典的形式
{'a': 1, 'b': 2, 'd': 4, 'c': 3}
>>> 

當這兩種形式混用時,可以給函數帶來極大的靈活性:

>>> def func(a,b,*c,**d):
...     print('a,b:',a,b)
...     print('c:',c)
...     print('d:',d)
... 
>>> func(1,2)
a,b: 1 2
c: ()
d: {}
>>> func(1,2,3,4,5,6,7,z=1,x=3,y=4)
a,b: 1 2
c: (3, 4, 5, 6, 7)
d: {'z': 1, 'x': 3, 'y': 4}
>>> 

解包參數

相反的,當你在調用函數時,使用*arg或**arg的語法,可以達到解包參數的效果(函數定義時使用這兩種形式,就像是把多餘的參數打包成爲列表或字典的形式,傳遞進入函數中)。
調用時使用*arg,就是把列表中的元素,分解爲一個個值,傳遞給函數參數,同理,使用**arg時,會以鍵/值的形式解包一個字典,並將其以關鍵字的形式傳遞給函數參數:

>>> def func(a,b,c,d):
...     print('a,b,c,d:',a,b,c,d)
... 
>>> l=[1,2,4,3]
>>> func(*l)
a,b,c,d: 1 2 4 3
>>> l={'d':99,'a':33,'b':22,'c':11}
>>> func(**l)
a,b,c,d: 33 22 11 99

keyword-only參數

在函數定義時,在*arg或*之後的參數,都必須使用關鍵字的形式傳遞參數值。在沒有默認值的情況下,這些參數都必須通過關鍵字的形式賦值:


>>> def func(a,b,*,c,d):       #c,d必須使用關鍵字的形式來賦值
...     print('a,b,c,d:',a,b,c,d)
... 
>>> func(1,2,c=4,d=6)
a,b,c,d: 1 2 4 6
>>> func(1,2,4,6)         #不使用關鍵字就會報錯,從語法上來講,4,6這兩個數已經被打包至“*”列表中。
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func() takes 2 positional arguments but 4 were given
>>> 

需要注意的是,**arg這樣的形式,只能出現在函數參數列表的最後面。不滿足這樣的要求都會報錯。

小結

本章中的內容介紹了函數中的參數,瞭解到Python是通過傳遞引用給參數的。在參數傳遞的過程中有很多種參數匹配的模式,這些模式使得Python程序由很好的靈活性。還有更高級的擴展:包括默認參數、關鍵字參數、多個任意參數的工具。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章