在如下方法定義中, *
和**
對param2
有何做用? html
def foo(param1, *param2): def bar(param1, **param2):
*
和**
在函數參數列表中有特殊用法。 *
表示參數是列表, **
表示參數是字典。 這容許函數接受任意數量的參數 python
**
(雙星)和*
(星)對參數有什麼做用
它們容許定義函數以接受和容許用戶傳遞任意數量的參數,位置( *
)和關鍵字( **
)。 數據結構
*args
容許任意數量的可選位置參數(參數),這些參數將分配給名爲args
的元組。 app
**kwargs
容許任意數量的可選關鍵字參數(參數),這些參數將在名爲kwargs
的字典中。 框架
您能夠(而且應該)選擇任何適當的名稱,可是若是要使參數具備非特定的語義,則args
和kwargs
是標準名稱。 函數
您也可使用*args
和**kwargs
分別從列表(或任何可迭代的)和字典(或任何映射)中傳入參數。 post
接收參數的函數沒必要知道它們正在擴展。 ui
例如,Python 2的xrange並不明確指望*args
,可是由於它須要3個整數做爲參數: this
>>> x = xrange(3) # create our *args - an iterable of 3 integers >>> xrange(*x) # expand here xrange(0, 2, 2)
再舉一個例子,咱們能夠在str.format
使用dict擴展: 編碼
>>> foo = 'FOO' >>> bar = 'BAR' >>> 'this is foo, {foo} and bar, {bar}'.format(**locals()) 'this is foo, FOO and bar, BAR'
您能夠在*args
以後使用僅關鍵字 *args
-例如,在這裏,必須將kwarg2
做爲關鍵字參數-而不是位置:
def foo(arg, kwarg=None, *args, kwarg2=None, **kwargs): return arg, kwarg, args, kwarg2, kwargs
用法:
>>> foo(1,2,3,4,5,kwarg2='kwarg2', bar='bar', baz='baz') (1, 2, (3, 4, 5), 'kwarg2', {'bar': 'bar', 'baz': 'baz'})
另外, *
能夠單獨使用*
表示僅緊隨關鍵字的關鍵字,而不容許無限的位置自變量。
def foo(arg, kwarg=None, *, kwarg2=None, **kwargs): return arg, kwarg, kwarg2, kwargs
在這裏, kwarg2
再次必須是一個明確命名的關鍵字參數:
>>> foo(1,2,kwarg2='kwarg2', foo='foo', bar='bar') (1, 2, 'kwarg2', {'foo': 'foo', 'bar': 'bar'})
並且咱們再也不能夠接受無限的位置參數,由於咱們沒有*args*
:
>>> foo(1,2,3,4,5, kwarg2='kwarg2', foo='foo', bar='bar') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: foo() takes from 1 to 2 positional arguments but 5 positional arguments (and 1 keyword-only argument) were given
再次,更簡單地說,在這裏咱們要求kwarg
必須使用名稱而不是位置:
def bar(*, kwarg=None): return kwarg
在此示例中,咱們看到若是嘗試經過位置傳遞kwarg
,則會出現錯誤:
>>> bar('kwarg') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: bar() takes 0 positional arguments but 1 was given
咱們必須顯式地將kwarg
參數做爲關鍵字參數傳遞。
>>> bar(kwarg='kwarg') 'kwarg'
*args
(一般稱爲「 star-args」)和**kwargs
(能夠經過說「 kwargs」來暗示星號,但對於「 double-star kwargs」能夠明確表示)是Python使用*
和**
表示法的常見用法。 這些特定的變量名稱不是必需的(例如,您可使用*foos
和**bars
),可是背離約定可能會激怒您的Python編碼人員。
咱們一般在不知道函數要接收什麼或咱們可能傳遞多少個參數時使用它們,有時甚至即便分別命名每一個變量也會變得很是混亂和多餘(但這是一般顯式的狀況比隱式更好)。
例子1
如下功能描述瞭如何使用它們,並演示了行爲。 注意,命名的b
參數將由前面的第二個位置參數使用:
def foo(a, b=10, *args, **kwargs): ''' this function takes required argument a, not required keyword argument b and any number of unknown positional arguments and keyword arguments after ''' print('a is a required argument, and its value is {0}'.format(a)) print('b not required, its default value is 10, actual value: {0}'.format(b)) # we can inspect the unknown arguments we were passed: # - args: print('args is of type {0} and length {1}'.format(type(args), len(args))) for arg in args: print('unknown arg: {0}'.format(arg)) # - kwargs: print('kwargs is of type {0} and length {1}'.format(type(kwargs), len(kwargs))) for kw, arg in kwargs.items(): print('unknown kwarg - kw: {0}, arg: {1}'.format(kw, arg)) # But we don't have to know anything about them # to pass them to other functions. print('Args or kwargs can be passed without knowing what they are.') # max can take two or more positional args: max(a, b, c...) print('e.g. max(a, b, *args) \n{0}'.format( max(a, b, *args))) kweg = 'dict({0})'.format( # named args same as unknown kwargs ', '.join('{k}={v}'.format(k=k, v=v) for k, v in sorted(kwargs.items()))) print('e.g. dict(**kwargs) (same as {kweg}) returns: \n{0}'.format( dict(**kwargs), kweg=kweg))
咱們可使用help(foo)
來檢查在線幫助中該函數的簽名,它告訴咱們
foo(a, b=10, *args, **kwargs)
讓咱們用foo(1, 2, 3, 4, e=5, f=6, g=7)
調用此函數
打印:
a is a required argument, and its value is 1 b not required, its default value is 10, actual value: 2 args is of type <type 'tuple'> and length 2 unknown arg: 3 unknown arg: 4 kwargs is of type <type 'dict'> and length 3 unknown kwarg - kw: e, arg: 5 unknown kwarg - kw: g, arg: 7 unknown kwarg - kw: f, arg: 6 Args or kwargs can be passed without knowing what they are. e.g. max(a, b, *args) 4 e.g. dict(**kwargs) (same as dict(e=5, f=6, g=7)) returns: {'e': 5, 'g': 7, 'f': 6}
例子2
咱們還可使用另外一個函數來調用它,咱們只提供a
:
def bar(a): b, c, d, e, f = 2, 3, 4, 5, 6 # dumping every local variable into foo as a keyword argument # by expanding the locals dict: foo(**locals())
bar(100)
打印:
a is a required argument, and its value is 100 b not required, its default value is 10, actual value: 2 args is of type <type 'tuple'> and length 0 kwargs is of type <type 'dict'> and length 4 unknown kwarg - kw: c, arg: 3 unknown kwarg - kw: e, arg: 5 unknown kwarg - kw: d, arg: 4 unknown kwarg - kw: f, arg: 6 Args or kwargs can be passed without knowing what they are. e.g. max(a, b, *args) 100 e.g. dict(**kwargs) (same as dict(c=3, d=4, e=5, f=6)) returns: {'c': 3, 'e': 5, 'd': 4, 'f': 6}
示例3:裝飾器中的實際用法
好的,因此也許咱們尚未看到該實用程序。 所以,假設您在區分代碼以前和/或以後有多個帶有冗餘代碼的功能。 爲了說明的目的,如下命名函數只是僞代碼。
def foo(a, b, c, d=0, e=100): # imagine this is much more code than a simple function call preprocess() differentiating_process_foo(a,b,c,d,e) # imagine this is much more code than a simple function call postprocess() def bar(a, b, c=None, d=0, e=100, f=None): preprocess() differentiating_process_bar(a,b,c,d,e,f) postprocess() def baz(a, b, c, d, e, f): ... and so on
咱們也許能夠用不一樣的方式處理此問題,可是咱們固然能夠用裝飾器提取冗餘,所以下面的示例演示了*args
和**kwargs
如何很是有用:
def decorator(function): '''function to wrap other functions with a pre- and postprocess''' @functools.wraps(function) # applies module, name, and docstring to wrapper def wrapper(*args, **kwargs): # again, imagine this is complicated, but we only write it once! preprocess() function(*args, **kwargs) postprocess() return wrapper
如今,因爲咱們考慮了冗餘性,每一個包裝函數均可以更加簡潔地編寫:
@decorator def foo(a, b, c, d=0, e=100): differentiating_process_foo(a,b,c,d,e) @decorator def bar(a, b, c=None, d=0, e=100, f=None): differentiating_process_bar(a,b,c,d,e,f) @decorator def baz(a, b, c=None, d=0, e=100, f=None, g=None): differentiating_process_baz(a,b,c,d,e,f, g) @decorator def quux(a, b, c=None, d=0, e=100, f=None, g=None, h=None): differentiating_process_quux(a,b,c,d,e,f,g,h)
經過分解代碼,使*args
和**kwargs
能夠執行代碼,咱們減小了代碼行,提升了可讀性和可維護性,而且在程序中的邏輯上擁有惟一的規範位置。 若是須要更改此結構的任何部分,則能夠在一個位置進行每次更改。
除函數調用外,* args和** kwargs在類層次結構中頗有用,而且還避免了必須在Python中編寫__init__
方法。 在相似Django代碼的框架中能夠看到相似的用法。
例如,
def __init__(self, *args, **kwargs): for attribute_name, value in zip(self._expected_attributes, args): setattr(self, attribute_name, value) if kwargs.has_key(attribute_name): kwargs.pop(attribute_name) for attribute_name in kwargs.viewkeys(): setattr(self, attribute_name, kwargs[attribute_name])
子類能夠是
class RetailItem(Item): _expected_attributes = Item._expected_attributes + ['name', 'price', 'category', 'country_of_origin'] class FoodItem(RetailItem): _expected_attributes = RetailItem._expected_attributes + ['expiry_date']
而後將該子類實例化爲
food_item = FoodItem(name = 'Jam', price = 12.0, category = 'Foods', country_of_origin = 'US', expiry_date = datetime.datetime.now())
另外,具備僅對該子類實例有意義的新屬性的子類能夠調用基類__init__
來卸載屬性設置。 這是經過* args和** kwargs完成的。 主要使用kwargs,以便使用命名參數能夠讀取代碼。 例如,
class ElectronicAccessories(RetailItem): _expected_attributes = RetailItem._expected_attributes + ['specifications'] # Depend on args and kwargs to populate the data as needed. def __init__(self, specifications = None, *args, **kwargs): self.specifications = specifications # Rest of attributes will make sense to parent class. super(ElectronicAccessories, self).__init__(*args, **kwargs)
能夠被形容爲
usb_key = ElectronicAccessories(name = 'Sandisk', price = '$6.00', category = 'Electronics', country_of_origin = 'CN', specifications = '4GB USB 2.0/USB 3.0')
完整的代碼在這裏
在Python 3.5中,還能夠在list
, dict
, tuple
和set
display(有時也稱爲文字)中使用此語法。 請參閱PEP 488:其餘拆包概述 。
>>> (0, *range(1, 4), 5, *range(6, 8)) (0, 1, 2, 3, 5, 6, 7) >>> [0, *range(1, 4), 5, *range(6, 8)] [0, 1, 2, 3, 5, 6, 7] >>> {0, *range(1, 4), 5, *range(6, 8)} {0, 1, 2, 3, 5, 6, 7} >>> d = {'one': 1, 'two': 2, 'three': 3} >>> e = {'six': 6, 'seven': 7} >>> {'zero': 0, **d, 'five': 5, **e} {'five': 5, 'seven': 7, 'two': 2, 'one': 1, 'three': 3, 'six': 6, 'zero': 0}
它還容許在單個函數調用中解壓縮多個可迭代對象。
>>> range(*[1, 10], *[2]) range(1, 10, 2)
(感謝mgilson的PEP連接。)
首先讓咱們瞭解什麼是位置參數和關鍵字參數。 下面是帶有位置參數的函數定義的示例。
def test(a,b,c): print(a) print(b) print(c) test(1,2,3) #output: 1 2 3
所以,這是帶有位置參數的函數定義。 您也可使用關鍵字/命名參數來調用它:
def test(a,b,c): print(a) print(b) print(c) test(a=1,b=2,c=3) #output: 1 2 3
如今讓咱們研究一個帶有關鍵字參數的函數定義示例:
def test(a=0,b=0,c=0): print(a) print(b) print(c) print('-------------------------') test(a=1,b=2,c=3) #output : 1 2 3 -------------------------
您也可使用位置參數調用此函數:
def test(a=0,b=0,c=0): print(a) print(b) print(c) print('-------------------------') test(1,2,3) # output : 1 2 3 ---------------------------------
所以,咱們如今知道帶有位置參數以及關鍵字參數的函數定義。
如今讓咱們研究「 *」運算符和「 **」運算符。
請注意,這些運算符能夠在兩個區域中使用:
a) 函數調用
b) 功能定義
在函數調用中使用「 *」運算符和「 **」運算符。
讓咱們直接看一個例子,而後討論它。
def sum(a,b): #receive args from function calls as sum(1,2) or sum(a=1,b=2) print(a+b) my_tuple = (1,2) my_list = [1,2] my_dict = {'a':1,'b':2} # Let us unpack data structure of list or tuple or dict into arguments with help of '*' operator sum(*my_tuple) # becomes same as sum(1,2) after unpacking my_tuple with '*' sum(*my_list) # becomes same as sum(1,2) after unpacking my_list with '*' sum(**my_dict) # becomes same as sum(a=1,b=2) after unpacking by '**' # output is 3 in all three calls to sum function.
因此記住
在函數調用中使用「 *」或「 **」運算符時-
'*'運算符將列表或元組等數據結構解壓縮爲函數定義所需的參數。
'**'運算符將字典分解成函數定義所需的參數。
如今讓咱們研究函數定義中使用'*'運算符。 例:
def sum(*args): #pack the received positional args into data structure of tuple. after applying '*' - def sum((1,2,3,4)) sum = 0 for a in args: sum+=a print(sum) sum(1,2,3,4) #positional args sent to function sum #output: 10
在函數定義中 ,「 *」運算符將接收到的參數打包到一個元組中。
如今讓咱們看一下函數定義中使用的「 **」示例:
def sum(**args): #pack keyword args into datastructure of dict after applying '**' - def sum({a:1,b:2,c:3,d:4}) sum=0 for k,v in args.items(): sum+=v print(sum) sum(a=1,b=2,c=3,d=4) #positional args sent to function sum
在函數定義中 ,「 **」運算符將接收到的參數打包到字典中。
所以請記住:
在函數調用中 ,「 *」將元組或列表的數據結構解壓縮爲位置或關鍵字參數,以供函數定義接收。
在函數調用中 ,「 **」將字典的數據結構解壓縮爲位置或關鍵字參數,以供函數定義接收。
在函數定義中 ,「 *」 將位置參數打包到元組中。
在函數定義中 ,「 **」 將關鍵字參數打包到字典中。