*args和**kwargs是什么意思?

def foo(x, y, *args):
def bar(x, y, **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')

完整的代码在这里

其他回答

TL;博士

它将传递给函数的参数分别打包到函数体中的list和dict中。当您这样定义函数签名时:

def func(*args, **kwds):
    # do stuff

它可以用任意数量的参数和关键字参数调用。非关键字参数被打包到函数体内名为args的列表中,关键字参数被包装到函数体内称为kwds的dict中。

func("this", "is a list of", "non-keyowrd", "arguments", keyword="ligma", options=[1,2,3])

现在在函数体内部,当调用函数时,有两个局部变量,args是一个值为[“this”,“is a list of”,“non-keyword”,“arguments”]的列表,kwds是一个具有值为{“keyword”:“ligma”,“options”:[1,2]}的dict


这也反过来起作用,即从呼叫者一侧起。例如,如果函数定义为:

def f(a, b, c, d=1, e=10):
    # do stuff

您可以通过打开调用范围中的可迭代项或映射来调用它:

iterable = [1, 20, 500]
mapping = {"d" : 100, "e": 3}
f(*iterable, **mapping)
# That call is equivalent to
f(1, 20, 500, d=100, e=3)

上下文

python 3.x使用打开包装**与字符串格式一起使用

与字符串格式一起使用

除了本主题中的答案之外,还有一个其他地方没有提到的细节。这扩展了Brad Solomon的答案

使用python str.format时,使用**解包也很有用。

这有点类似于使用python f-string f-string所做的操作,但增加了声明dict以保存变量的开销(f-string不需要dict)。

快速示例

  ## init vars
  ddvars = dict()
  ddcalc = dict()
  pass
  ddvars['fname']     = 'Huomer'
  ddvars['lname']     = 'Huimpson'
  ddvars['motto']     = 'I love donuts!'
  ddvars['age']       = 33
  pass
  ddcalc['ydiff']     = 5
  ddcalc['ycalc']     = ddvars['age'] + ddcalc['ydiff']
  pass
  vdemo = []

  ## ********************
  ## single unpack supported in py 2.7
  vdemo.append('''
  Hello {fname} {lname}!

  Today you are {age} years old!

  We love your motto "{motto}" and we agree with you!
  '''.format(**ddvars)) 
  pass

  ## ********************
  ## multiple unpack supported in py 3.x
  vdemo.append('''
  Hello {fname} {lname}!

  In {ydiff} years you will be {ycalc} years old!
  '''.format(**ddvars,**ddcalc)) 
  pass

  ## ********************
  print(vdemo[-1])

TL;博士

以下是python编程中*和**的6种不同用例:

要使用*args:def foo(*args):pass接受任意数量的位置参数,这里foo接受任意数目的位置参数,即,以下调用是有效的foo(1)、foo(2,'bar')若要使用**kwargs:def foo(**kwargs):pass接受任意数量的关键字参数,此处“foo”接受任意数量关键字参数,即,以下调用是有效的foo(name='Tom')、foo(name='Tom',age=33)要使用*args,**kwargs:def foo(*args、**kwargs):pass接受任意数量的位置和关键字参数,这里foo接受任意数目的位置和关键词参数,即,以下调用是有效的foo(1,name='Tom')、foo(2,'bar',name='Tom',age=33)要使用*:def foo(pos1,pos2,*,kwarg1):pass强制只接受关键字参数,这里*表示foo只接受pos2之后的关键字参数,因此foo(1,2,3)引发TypeError,但foo(1,2,kwarg1=3)正常。为了表示对更多位置参数不再感兴趣,请使用*_(注意:这只是一个约定):def foo(bar,baz,*_):pass表示(按照约定)foo在工作中只使用bar和baz参数,而忽略其他参数。为了表示对更多关键字参数不再感兴趣,请使用**_(注意:这只是一个约定):def foo(bar,baz,**_):pass意味着(按照约定)foo在工作中只使用bar和baz参数,而忽略其他参数。

好处:从python 3.8开始,可以在函数定义中使用/来强制执行仅限位置的参数。在以下示例中,参数a和b仅是位置性的,而c或d可以是位置性或关键字,e或f必须是关键字:

def f(a, b, /, c, d, *, e, f):
    pass

好处2:对同一个问题的回答也带来了一个新的视角,它分享了*和**在函数调用、函数签名、for循环等方面的含义。

*args是一个特殊的参数,可以将0个或多个(位置)参数作为元组。**kwargs是一个特殊的参数,可以将0个或多个(关键字)参数作为字典。

*在Python中,有两种参数位置参数和关键字参数:

*参数:

例如,*args可以采用0个或多个参数作为元组,如下所示:

           ↓
def test(*args):
    print(args)

test() # Here
test(1, 2, 3, 4) # Here
test((1, 2, 3, 4)) # Here
test(*(1, 2, 3, 4)) # Here

输出:

()
(1, 2, 3, 4)
((1, 2, 3, 4),)
(1, 2, 3, 4)

并且,当打印*参数时,将打印4个数字,不带括号和逗号:

def test(*args):
    print(*args) # Here
 
test(1, 2, 3, 4)

输出:

1 2 3 4

并且,args具有元组类型:

def test(*args):
    print(type(args)) # Here
 
test(1, 2, 3, 4)

输出:

<class 'tuple'>

但是,*参数没有类型:

def test(*args):
    print(type(*args)) # Here
 
test(1, 2, 3, 4)

输出(错误):

TypeError:type()需要1或3个参数

并且,正常参数可以放在*args之前,如下所示:

          ↓     ↓
def test(num1, num2, *args):
    print(num1, num2, args)
    
test(1, 2, 3, 4)

输出:

1 2 (3, 4)

但是,**kwargs不能放在*args之前,如下所示:

             ↓     
def test(**kwargs, *args):
    print(kwargs, args)
    
test(num1=1, num2=2, 3, 4)

输出(错误):

语法错误:无效语法

而且,正常参数不能放在*args之后,如下所示:

                 ↓     ↓
def test(*args, num1, num2):
    print(args, num1, num2)
    
test(1, 2, 3, 4)

输出(错误):

TypeError:test()缺少2个必需的仅关键字参数:“num1”和“num2”

但是,如果正常参数具有默认值,则可以将它们放在*args之后,如下所示:

                      ↓         ↓
def test(*args, num1=100, num2=None):
    print(args, num1, num2)
    
test(1, 2, num1=3, num2=4)

输出:

(1, 2) 3 4

此外,**kwargs可以放在*args之后,如下所示:

                    ↓
def test(*args, **kwargs):
    print(args, kwargs)
    
test(1, 2, num1=3, num2=4)

输出:

(1, 2) {'num1': 3, 'num2': 4}

**克瓦格斯:

例如,**kwargs可以使用0个或多个参数作为字典,如下所示:

             ↓
def test(**kwargs):
    print(kwargs)

test() # Here
test(name="John", age=27) # Here
test(**{"name": "John", "age": 27}) # Here

输出:

{}
{'name': 'John', 'age': 27}
{'name': 'John', 'age': 27}

并且,当打印*kwargs时,将打印两个键:

def test(**kwargs):
    print(*kwargs) # Here
 
test(name="John", age=27)

输出:

name age

并且,kwargs具有dict类型:

def test(**kwargs):
    print(type(kwargs)) # Here
 
test(name="John", age=27)

输出:

<class 'dict'>

但是,*kwargs和**kwargs没有类型:

def test(**kwargs):
    print(type(*kwargs)) # Here
 
test(name="John", age=27)
def test(**kwargs):
    print(type(**kwargs)) # Here
 
test(name="John", age=27)

输出(错误):

TypeError:type()需要1或3个参数

并且,正常参数可以放在**kwargs之前,如下所示:

          ↓     ↓
def test(num1, num2, **kwargs):
    print(num1, num2, kwargs)

test(1, 2, name="John", age=27)

输出:

1 2 {'name': 'John', 'age': 27}

此外,*args可以放在**kwargs之前,如下所示:

           ↓
def test(*args, **kwargs):
    print(args, kwargs)

test(1, 2, name="John", age=27)

输出:

(1, 2) {'name': 'John', 'age': 27}

并且,正常参数和*args不能放在**kwargs之后,如下所示:

                    ↓     ↓
def test(**kwargs, num1, num2):
    print(kwargs, num1, num2)

test(name="John", age=27, 1, 2)
                     ↓
def test(**kwargs, *args):
    print(kwargs, args)

test(name="John", age=27, 1, 2)

输出(错误):

语法错误:无效语法

对于*args和**kwargs:

实际上,您可以为*args和**kwargs使用其他名称,如下所示*args和**kwargs通常使用:

            ↓        ↓
def test(*banana, **orange):
    print(banana, orange)
    
test(1, 2, num1=3, num2=4)

输出:

(1, 2) {'num1': 3, 'num2': 4}

让我们首先了解什么是位置参数和关键字参数。下面是带有位置参数的函数定义示例。

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

在函数定义中,“**”运算符将收到的参数打包到字典中。

所以请记住:

在函数调用中,“*”将元组或列表的数据结构解包为要由函数定义接收的位置或关键字参数。

在函数调用中,“**”将字典的数据结构解包为函数定义要接收的位置或关键字参数。

在函数定义中,“*”将位置参数打包到元组中。

在函数定义中,“**”将关键字参数打包到字典中。