Python定義的函數(或調用)中參數*args 和**kwargs的用法


出處:http://blog.csdn.net/chenjinyu_tang/article/details/8136841


Python中*args 和**kwargs的用法


當函數的參數不確定時,可以使用*args 和**kwargs,*args 沒有key值,**kwargs有key值。

還是直接來代碼吧,廢話少說

[python] view plain copy
 print?
  1. def fun_var_args(farg, *args):  
  2.     print "arg:", farg  
  3.     for value in args:  
  4.         print "another arg:", value  
  5.   
  6. fun_var_args(1"two"3# *args可以當作可容納多個變量組成的list  
result:

[python] view plain copy
 print?
  1. arg: 1  
  2. another arg: two  
  3. another arg: 3  

**kwargs:

[python] view plain copy
 print?
  1. def fun_var_kwargs(farg, **kwargs):  
  2.     print "arg:", farg  
  3.     for key in kwargs:  
  4.         print "another keyword arg: %s: %s" % (key, kwargs[key])  
  5.   
  6.   
  7. fun_var_kwargs(farg=1, myarg2="two", myarg3=3# myarg2和myarg3被視爲key, 感覺**kwargs可以當作容納多個key和value的dictionary  

result:

[python] view plain copy
 print?
  1. arg: 1  
  2. another keyword arg: myarg2: two  
  3. another keyword arg: myarg3: 3  
也可以用下面的形式:

[python] view plain copy
 print?
  1. def fun_var_args_call(arg1, arg2, arg3):  
  2.     print "arg1:", arg1  
  3.     print "arg2:", arg2  
  4.     print "arg3:", arg3  
  5.   
  6. args = ["two"3#list  
  7.   
  8. fun_var_args_call(1, *args)  
result:

[python] view plain copy
 print?
  1. arg1: 1  
  2. arg2: two  
  3. arg3: 3  

[python] view plain copy
 print?
  1. def fun_var_args_call(arg1, arg2, arg3):  
  2.     print "arg1:", arg1  
  3.     print "arg2:", arg2  
  4.     print "arg3:", arg3  
  5.   
  6. kwargs = {"arg3"3"arg2""two"# dictionary  
  7.   
  8. fun_var_args_call(1, **kwargs)  
result:

[python] view plain copy
 print?
  1. arg1: 1  
  2. arg2:"two"  
  3. arg3:3  

出處:http://blog.csdn.net/callinglove/article/details/45483097

理解'*','*args','**','**kwargs'

原文Understanding ‘*’, ‘args’,’*‘and’**kwargs’

剛開始學習Python的時候,對有關args,kwargs,和*的使用感到很困惑。相信對此感到疑惑的人也有很多。我打算通過這個帖子來排解這個疑惑(希望能減少疑惑)。

讓我們通過以下5步來理解: 
1. 通過一個函數調用來理解’*’的作用 
2. 通過一個函數的定義來理解’*args’的含義 
3. 通過一個函數的調用來理解’**’的作用 
4. 通過一個函數的定義來解’**kwargs’的含義 
5. 通過一個應用實例來說明’args’,’kwargs’應用場景以及爲何要使用它


通過一個函數調用來理解’*’的作用

定義一個含三個位置參數的函數”fun”.

>>> def fun(a,b,c):
...     print a,b,c
... 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

傳三個位置參數調用此函數

>>> fun(1,2,3)
1 2 3       #輸出
  • 1
  • 2
  • 1
  • 2

可以看到出入三個位置參數調用此函數,會打印出三個參數

現在我們定義一個含三個整數的數列,並使用’*’

>>> l = [1,2,3]
>>> fun(*l)
1 2 3       #輸出
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

‘*’ 做了什麼?

它拆開數列’l’的數值作爲位置參數,並吧這些位置參數傳給函數’fun’來調用。

因此拆數列、傳位置參數意味着fun(*l)與fun(1,2,3)是等效的,因爲l = [1,2,3]。

試着數列中使用其他數值

>>> l = [4,8,0]
>>> fun(*l)
4 8 0       #輸出
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

接下來我們試着在數列中放四個數值,調用函數會出現什麼情況呢

>>> l = [3,6,9,1]
>>> fun(*l)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: fun() takes exactly 3 arguments (4 given)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

在這次調用中我們並沒有得到合適的結果,觸發了TypeWrror異常。很容易看到錯誤內容”fun() takes exactly 3 arguments (4 given)”.

爲什麼會發生這種情況呢?

數列’l’含有四個數值.因此,我們試圖調用’fun(*l)’,’l’中數值拆開傳給函數fun作爲位置參數。但是,’l’中有四個數值,調用’fun(*l)’相當於調用’fun(3,6,9,1)’,又因爲函數’fun’定義中只用三個位置參數,因此我們得到這個錯誤。同理,同樣的步驟,數列’l’中有兩個數值情況,注意error內容。

>>> l = [7,4]
>>> fun(*l)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: fun() takes exactly 3 arguments (2 given)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

‘*l’與位置參數混合使用

>>> fun(23, *l)
23 7 4
  • 1
  • 2
  • 1
  • 2

在這裏,我們給出一個位置參數23,和從數列’l’拆除的兩個數值7和4,因此三個參數23,7和4傳給了函數’fun’

通過一個函數的定義來理解’*args’的含義

修改函數的定義:

>>> def fun(*args):
...     print args
... 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

傳一個位置參數調用此函數

>>> fun(13)
(13,)
  • 1
  • 2
  • 1
  • 2

傳多個參數調用此函數

>>> fun(11,93,43)
(11, 93, 43)
  • 1
  • 2
  • 1
  • 2

‘*args’在函數定義中是做什麼用的?

它接收元組作爲位置參數,而非是常見的參數列表。在這裏,”args”是個元組。在我們解釋中不要擔心”常見的參數”這部分的理解,這個會在接下來的例子中逐漸明瞭。在上個例子中,調用函數打印”args”時,他會打印元組中包含的所有數值。

我們重新定義函數,”*args”與”常規參數列表”混合使用

>>> def fun(a, *args):
...     print "a is ", a
...     print "args is ", args
... 
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

在這個函數定義中,參數”a”代表”常規參數列表”。 
傳四個位置參數調用此函數:

>>> fun(11,12,34,43)
a is  11
args is  (12, 34, 43)
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

很容易看到,’a’打印出爲11,即第一個位置參數。’a’之後只一個參數’*args’.因此,’args’接收除常規參數之外的位置參數作爲元組。因此元組args作爲元組接收12,34和43。

我們也可以傳一個位置參數來調用此函數:

>>> fun(91)
a is  91
args is  ()
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

在這裏,我們傳的唯一一個參數分配給了常規參數’a’.因此,’args’接收到一個空元組。

既然我們獲取了”args”,我們可以提取需要的數值來做我們想做的事情。重新定義”fun”:

>>> def fun(a, *args):
...     print a
...     print "args can receive a tuple of any number of arguments, let's print all that."
...     for arg in args:
...             print arg
... 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

現在我們傳任意個參數來調用此函數:

>>> fun(1,5,6,7)
1
args can receive a tuple of any number of arguments, let's print all that.
5
6
7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

‘args’既然是元組,我們就可以遍歷它。

現在我們考慮使用所有能得到的參數的場景。我們需要使用兩個函數,第一個函數帶有任意個參數,並通過另外一個函數計算除第一參數的其他參數之和。奇怪的用例,但我們只需回顧我們目前所做的。我們的目的就是在一個函數中獲取可變參數,並把這些參數餐給另一個函數。

第一步我們寫一個函數計算和。在這個用例中,這個函數會在第一個函數中應用。

>>> def calculate_sum(*args):
...     return sum(args)
... 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

在這個函數中,我們使用內建函數’sum’,它使用元組或數列作爲參數,返回元組所有元素的和。從函數的定義可以看出’args’接收包含傳給此函數位置參數的元組.因此,’args’是一個元組,簡介的作爲函數’sum’的參數。接下來定義另外一個函數,該函數有任意個參數,並利用上一個函數來計算除第一個參數之外的其他參數的和。

>>> def ignore_first_calculate_sum(a,*iargs):
...     required_sum = calculate_sum(*iargs)
...     print "sum is ", required_sum
... 
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

我們可以傳任意個參數給這個函數。第一個參數被常規參數’a’接收,其他參數被’iargs’作爲元組接收。正如我們考慮的案例,計算除第一個參數之外的其他參數的和。因此,我們用’a’接收第一個參數,’iargs’是包含其他參數的元組。我們用到函數’calculate_sum’,但’calculate_sum’需要多個位置參數作爲元組傳給’args’。所以在函數’ignore_first_calculate_sum’需要拆元組’iargs’,然後將元素作爲位置參數傳給’calculate_sum’.注意,用’*’拆元組。

所以,我們這樣調用’required_sum=calculate_sum(*iargs)’.

‘required_sum=calculate_sum(iargs)’不能這麼調用,因爲傳給’calculate_sum’之前我們需要unpack數值。不使用’*’將不會unpack數值,也就不能執行想要的動作。調用函數如下:

>>> ignore_first_calculate_sum(12, 1,4,5)
sum is  10
>>> ignore_first_calculate_sum()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: ignore_first_calculate_sum() takes at least 1 argument (0 given)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

得到想要的結果。

通過一個函數的調用來理解’**’的作用

定義一個三個參數的函數,並用多種方式調用:

>>> def fun(a, b, c):
...     print a, b, c
... 
>>> fun(1,5,7)
1 5 7
>>> fun(a=1,b=5,c=7)
1 5 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

使用”**”調用函數,這種方式我們需要一個字典.注意:在函數調用中使用”*”,我們需要元組;在函數調用中使用”**”,我們需要一個字典

>>> d={'b':5, 'c':7}
>>> fun(1, **d)
1 5 7
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

在函數調用中”**”做了什麼?

它unpack字典,並將字典中的數據項作爲鍵值參數傳給函數。因此,”fun(1, **d)”的寫法與”fun(1, b=5, c=7)”等效. 
爲了更好的理解再多舉幾個例子:

>>> d = {'c':3}
>>> fun(1, 4, **d)
1 4 3
>>> d = {'a':7, 'b':3, 'c':8}
>>> fun(**d)
7 3 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

讓我們製造一些錯誤:

>>> d = {'a':7, 'b':3, 'c':8, 'd':90}
>>> fun(**d)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: fun() got an unexpected keyword argument 'd'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

這次調用等同於’fun(a=7, b=3, c=8, d=90)’,但函數只需要三個參數,因此我們得到TypeError

>>> d = {'a':7, 'b':3,'d':90}
>>> fun(**d)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: fun() got an unexpected keyword argument 'd'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

fun(**d)等同於fun(a=7, b=3, d=90).傳給函數”fun”想要的參數個數,但參數列表中並沒有’d’,調用中’d’鍵值參數傳給函數導致TypeError.

So, “*” unpacks the dictionary i.e the key values pairs in the dictionary as keyword arguments and these are sent as keyword arguments to the function being called. “” unpacks a list/tuple i.e the values in the list as positional arguments and these are sent as positional arguments to the function being called.

通過函數定義來理解’**kwargs’的含義

重定義函數”fun”:

>>> def fun(a, **kwargs):
...     print a, kwargs
... 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

此函數只用一個位置參數,因爲常規參數列表中只有一個變量’a’.但是通過”**kwargs”,可以傳多個鍵值參數。

>>> fun(1, b=4, c=5)
1 {'c': 5, 'b': 4}
>>> fun(45, b=6, c=7, d=8)
45 {'c': 7, 'b': 6, 'd': 8}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

在函數定義中”**kwargs”意味着什麼? 
用”**kwargs”定義函數,kwargs接收除常規參數列表職位的鍵值參數字典。在這裏’kwargs’是個字典。

重新定義函數:

>>> def fun(a, **kwargs):
...     print "a is ", a
...     print "We expect kwargs 'b' and 'c' in this function"
...     print "b is ", kwargs['b']
...     print "c is ", kwargs['c']
... 
>>> fun(1, b=3,c=5)
a is  1
We expect kwargs 'b' and 'c' in this function
b is  3
c is  5
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

錯誤調用:

>>> fun(1, b=3, d=5)
a is  1
We expect kwargs 'b' and 'c' in this function
b is  3
c is 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in fun
KeyError: 'c'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

上面的調用,位置參數’a’和鍵值參數’b’都打印出來了。傳入的其他鍵值參數是’d’,函數需要鍵值參數’c’,並從字典’kwargs’獲取。但沒有傳入鍵值’c’,引發KeyError.如果傳入了鍵值’c’就不會引發這個錯誤

>>> fun(1, b=3, d=5, c=9)
a is  1
We expect kwargs 'b' and 'c' in this function
b is  3
c is  9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

由於’**kwargs’在函數參數列表中,我們可以傳任意個鍵值參數。上面的調用傳入了”d”,但函數並沒用到。

另外一個錯誤:

>>> fun(1, {'b':2, 'c':34})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: fun() takes exactly 1 argument (2 given)
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

正如錯誤提示,函數’fun’只需要一個位置參數,卻給了兩個。儘管’kwargs’接收鍵值參數作爲一個字典,但你不能傳一個字典作爲位置參數給’kwargs’.你可以像下面那樣調用:

>>> fun(1, **{'b':2, 'c':34})
a is  1
We expect kwargs 'b' and 'c' in this function
b is  2
c is  34
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

在一個字典前使用”**”可以unpack字典,傳字典中的數據項作爲鍵值參數。

通過一個應用實例來說明’args’,’kwargs’應用場景以及爲何要使用它

在任何時候繼承類和重寫方法的,我們應當用到’*args’和’**kwargs’將接收到的位置參數和鍵值參數給父類方法。通過實例我們更好的理解

>>> class Model(object):
...     def __init__(self, name):
...             self.name = name
...     def save(self, force_update=False, force_insert=False):
...             if force_update and force_insert:
...                     raise ValueError("Cannot perform both operations")
...             if force_update:
...                     print "Updated an existing record"
...             if force_insert:
...                     print "Created a new record"
... 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

定義一個類,我們可以創建類的對象,類的對象有一個方法’save()’.假設類的對象可以通過save()方法保存到數據庫中。通過函數save()參數來決定是否在數據庫中創建一條記錄或者更新現存的記錄。 
構造一個新類,類有’Model’的行爲,但我們只有檢查一些條件後纔會保存這個類的對象。這個新類繼承’Model’,重寫’Model’的’save()’

>>> class Child(Model):
...     def save(self, *args, **kwargs):
...             if self.name=='abcd':
...                     super(ChildModel, self).save(*args, **kwargs)
...             else:
...                     return None
... 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

實際上對應的保存動作發生在’Model’的’save’方法中。所以我們調用子類的的’save()’方法而非’Model’的方法.子類ChildModel的’save()’接收任何父類save()需要的參數,並傳給父類方法。因此,子類’save()’方法參數列表中有”*args”和”**kwargs”,它們可以接收任意位置參數或鍵值參數,常規參數列表除外。

下面創建ChildModel實體並保存:

>>> c=ChildModel('abcd')
>>> c.save(force_insert=True)
Created a new record
>>> c.save(force_update=True)
Updated an existing record
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

這裏傳兼職參數給對象的save()方法。調用的是子類的save(),It received a dictionary containing the keyword argument in “kwargs”. Then it used “**” to unpack this dictionary as keyword arguments and then passed it to the superclass save(). So, superclass save() got a keyword argument ‘force_insert’ and acted accordingly.



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