*args和**kwargs的用法

1544

所以,我对*args**kwargs的概念有困难。

到目前为止我学到:

  • *args= 参数列表-作为位置参数
  • **kwargs= 字典-其键变成单独的关键字参数,值则成为这些参数的值。

我不明白这对编程任务有什么帮助。

也许:

我认为将列表和字典作为函数的参数同时作为通配符输入,这样我就可以传递任何参数?

是否有一个简单的例子来解释如何使用*args**kwargs?

我找到的教程只用了"*"和一个变量名。

*args**kwargs只是占位符还是在代码中确切使用*args**kwargs


13
一旦你掌握了这些,你就永远不想错过它们(特别是如果你曾经需要处理 PHP 的 func_*_args())。 - Boldewyn
5
顺便提一下,这些文档可以在 http://docs.python.org/faq/programming.html#id13 找到。 - mlvljr
3
其实,只要这两种参数格式位于函数声明的最后两个位置,它们就可以被添加到任何函数声明中。注意参数的顺序:先是 explicit args,然后是 *args,最后是 **kwargs。例如,def foo(arg1, arg2, *args, **kwargs): ... - smwikipedia
如果你正在使用JavaScript,那么有一个类似的arguments数组。 - NoBugs
在其他一些编程语言中,*args形式被称为可变参数。 - Mark Ransom
11个回答

1779

语法是使用***。名称*args**kwargs仅为约定俗成,但没有硬性要求使用它们。

当您不确定有多少参数将传递给函数时,可以使用*args,即它允许您向函数传递任意数量的参数。例如:

>>> def print_everything(*args):
        for count, thing in enumerate(args):
...         print( '{0}. {1}'.format(count, thing))
...
>>> print_everything('apple', 'banana', 'cabbage')
0. apple
1. banana
2. cabbage

**kwargs允许您处理未预先定义的命名参数:

>>> def table_things(**kwargs):
...     for name, value in kwargs.items():
...         print( '{0} = {1}'.format(name, value))
...
>>> table_things(apple = 'fruit', cabbage = 'vegetable')
cabbage = vegetable
apple = fruit

你也可以和命名参数一起使用。显式参数先获取值,然后剩余的所有参数都传递给*args**kwargs。命名参数在列表中排在前面。例如:

def table_things(titlestring, **kwargs)

在函数定义中,你也可以同时使用*args**kwargs,但*args必须出现在**kwargs之前。

调用函数时,你也可以使用***语法。例如:

>>> def print_three_things(a, b, c):
...     print( 'a = {0}, b = {1}, c = {2}'.format(a,b,c))
...
>>> mylist = ['aardvark', 'baboon', 'cat']
>>> print_three_things(*mylist)
a = aardvark, b = baboon, c = cat

从这个例子中可以看出,它将列表(或元组)打包并解压缩。通过这种方式,将其与函数中的参数进行匹配。当然,在函数定义和函数调用中都可以使用 *


15
在Python帮助文档中查找这个内容应该怎么做? - Alex
16
@Alex: 这里 - Mr_and_Mrs_D
1
似乎不可能在函数调用的中间作为位置参数扩展传递的列表,例如 function_call(arg1,arg2,*expanded_list_args,arg4,arg5)。我相信扩展列表只能跟随关键字参数。有没有办法绕过这个限制? - mlh3789
7
@mlh3789 是的,这个代码只适用于Python3。 但是有一点有点奇怪:这似乎可以用在赋值语句中: a, b, *c, d, e = 1, 2, 3, 4, 5, 6 会将c赋值为[3, 4]。有点令人困惑。 - Christian Tismer
它们之间有性能差异吗? - Deepam Gupta
显示剩余4条评论

513
< p >在子类化方面,使用*args**kwargs非常有用。
class Foo(object):
    def __init__(self, value1, value2):
        # do something with the values
        print value1, value2

class MyFoo(Foo):
    def __init__(self, *args, **kwargs):
        # do something else, don't care about the args
        print 'myfoo'
        super(MyFoo, self).__init__(*args, **kwargs)

这样,您可以扩展Foo类的行为,而无需过多了解Foo。如果您正在编程到可能更改的API,这可能非常方便。MyFoo只是将所有参数传递给Foo类。


141
只有当你已经理解*和**时,这个答案才有意义。 - Scott Tesler
7
我不明白。如果你的API发生变化,你仍然需要更改所有实例化子类的地方。如果你更改了所有这些地方,那么与其出于无意义的args和kwargs将子类的签名拼凑在一起,不如将子类的签名固定下来。你提到这不需要了解Foo的知识是没有意义的,因为一旦Foo构造函数的设置签名发生更改,所有MyFoo的实例化调用也必须更改。这需要了解Foo以及其构造函数所需的参数。 - Zoran Pavlovic
2
@ZoranPavlovic 这种情况的一个例子是,当您为现有产品提供附加组件并希望覆盖/扩展某些功能时。通过使用args和**kwargs,如果基础产品发生更改,此附加组件将继续运行。但是,实例化MyFoo发生在附加组件之外。这样说是否更有意义?(话虽如此:装饰器是可以使用args和**kwargs的更好示例。) - Mark van Lent
1
如何为子类的特定参数进行扩展? - Hanan Shteingart
1
@VIFI:你也一样。字典将作为args的第二个元素传入。如果要将它们作为关键字参数,则需要编写MyFoo(1, 2, 3, third=4, fifth=5)。而MyFoo(1,2,3,4,5)将创建一个args=(1,2,3,4,5)。请你们在发表完全错误的言论之前,先阅读文档并进行测试。 - Marco Sulla
显示剩余7条评论

341

这里有一个使用了三种不同参数类型的示例。

def func(required_arg, *args, **kwargs):
    # required_arg is a positional-only parameter.
    print required_arg

    # args is a tuple of positional arguments,
    # because the parameter name has * prepended.
    if args: # If args is not empty.
        print args

    # kwargs is a dictionary of keyword arguments,
    # because the parameter name has ** prepended.
    if kwargs: # If kwargs is not empty.
        print kwargs

>>> func()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func() takes at least 1 argument (0 given)

>>> func("required argument")
required argument

>>> func("required argument", 1, 2, '3')
required argument
(1, 2, '3')

>>> func("required argument", 1, 2, '3', keyword1=4, keyword2="foo")
required argument
(1, 2, '3')
{'keyword2': 'foo', 'keyword1': 4}

76

以下是我最喜欢使用 ** 语法的地方之一,就像 Dave Webb 最后的示例一样:

mynum = 1000
mystr = 'Hello World!'
print("{mystr} New-style formatting is {mynum}x more fun!".format(**locals()))

我不确定与仅使用名称本身相比是否非常快,但是这样打字更容易!


38
嘿,这个人在 Python 3.6 f-strings 变得流行之前就已经发明了它。 - cat
@cat 请解释一下?你具体指的是什么?即使在Python 2中,format方法不是已经存在了吗? - dabadaba
1
@dabadaba f-strings,而不是格式化字符串(例如 f'{mystr} 新式格式化更有趣,倍数为 {mynum}!' - Wayne Werner
4
@dabadaba 恭喜成为今天的“幸运10000”之一! - Wayne Werner
@dabadaba 请查看 https://www.python.org/dev/peps/pep-0498 - cat
显示剩余3条评论

49

*args和**kwargs有用的一个场景是编写包装函数(如装饰器),这些函数需要能够接受任意参数并将它们传递给被包装的函数。例如,一个简单的装饰器,可以打印被包装函数的参数和返回值:

def mydecorator( f ):
   @functools.wraps( f )
   def wrapper( *args, **kwargs ):
      print "Calling f", args, kwargs
      v = f( *args, **kwargs )
      print "f returned", v
      return v
   return wrapper

44

*args和**kwargs是Python的特殊魔法功能。想象一下一个可能有未知数目参数的函数。例如,出于种种原因,您想要编写一个函数,对未知数量的数字进行求和(并且您不想使用内置的sum函数)。那么您可以编写以下函数:

def sumFunction(*args):
  result = 0
  for x in args:
    result += x
  return result

并且可以这样使用:sumFunction(3,4,6,3,6,8,9)。

**kwargs有不同的功能。使用**kwargs,您可以向函数传递任意关键字参数,并将它们作为字典访问。

def someFunction(**kwargs):
  if 'text' in kwargs:
    print kwargs['text']

调用someFunction(text="foo")将打印出foo。


1
终于有一个足够简单的用例可以帮助我理解 *args**kwargs 了 XD。谢谢您。 - Quan Bui

20

想象一下你有一个函数,但你不想限制它所需要的参数数量。

>>> import operator
>>> def multiply(*args):
...  return reduce(operator.mul, args)

然后你可以像这样使用该函数:

>>> multiply(1,2,3)
6

or

>>> numbers = [1,2,3]
>>> multiply(*numbers)
6

18

*args**kwargs**kw这些名称纯粹是基于约定俗成的惯例。这样可以使我们更轻松地阅读彼此的代码。

有一个方便之处是在使用结构体模块时

struct.unpack()返回一个元组,而struct.pack()使用可变数量的参数。在操作数据时,将元组传递给struct.pack()非常方便。

tuple_of_data = struct.unpack(format_str, data)
# ... manipulate the data
new_data = struct.pack(format_str, *tuple_of_data)

如果没有这种能力,你将被迫编写:

new_data = struct.pack(format_str, tuple_of_data[0], tuple_of_data[1], tuple_of_data[2],...)

这也意味着如果 format_str 改变了,并且元组的大小也发生了改变,我将不得不回去编辑那个非常长的语句。


10

请注意,*args/**kwargs是函数调用语法的一部分,而不是真正的运算符。我遇到的一个特殊副作用是,您不能在print语句中使用*args扩展,因为print不是函数。

这似乎很合理:

def myprint(*args):
    print *args

很遗憾这段代码无法编译(语法错误)。

这段代码可以编译:

def myprint(*args):
    print args

但是输出的参数是元组形式,这不是我们想要的。

我采用的解决方案是:

def myprint(*args):
    for arg in args:
        print arg,
    print

8
当然,总是有 from __future__ import print_function :) - pfctdayelise
4
print是Python3中的一个函数。:) http://docs.python.org/3.0/whatsnew/3.0.html - sk8asd123

8

这些参数通常用于代理函数,以便代理可以将任何输入参数传递给目标函数。

def foo(bar=2, baz=5):
    print bar, baz

def proxy(x, *args, **kwargs): # reqire parameter x and accept any number of additional arguments
    print x
    foo(*args, **kwargs) # applies the "non-x" parameter to foo

proxy(23, 5, baz='foo') # calls foo with bar=5 and baz=foo
proxy(6)# calls foo with its default arguments
proxy(7, bar='asdas') # calls foo with bar='asdas' and leave baz default argument

但是由于这些参数隐藏了实际参数名称,最好还是避免使用它们。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接