在如下方法定义中, *
和**
对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
在函数定义中 ,“ **”运算符将接收到的参数打包到字典中。
所以请记住:
在函数调用中 ,“ *”将元组或列表的数据结构解压缩为位置或关键字参数,以供函数定义接收。
在函数调用中 ,“ **”将字典的数据结构解压缩为位置或关键字参数,以供函数定义接收。
在函数定义中 ,“ *” 将位置参数打包到元组中。
在函数定义中 ,“ **” 将关键字参数打包到字典中。