题 * args和** kwargs? [重复]


这个问题在这里已有答案:

所以我对概念有困难 *args 和 **kwargs

到目前为止,我已经了解到:

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

我不明白这会对哪些编程任务有所帮助。

也许:

我想输入列表和字典作为函数AND的参数同时作为通配符,所以我可以传递任何参数?

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

我发现的教程也使用了“*”和变量名。

*args 和 **kwargs 只是占位符或你是否完全使用 *args 和 **kwargs 在代码中?


1195
2017-08-03 08:28


起源


一旦你掌握了这些,你永远不会想念它们(特别是,如果你不得不处理PHP的话 func_*_args())。 - Boldewyn
你在读什么教程?请使用标题或您看到的教程链接更新您的问题。 - S.Lott
文档是在 docs.python.org/faq/programming.html#id13,顺便说一句。 - mlvljr
实际上,这两种参数格式可以添加到任何函数声明中,只要它们是最后一种。注意顺序: explicit args, then *args, then **kwargs。例如 def foo (arg1, arg2, *args, **kwargs): ... - smwikipedia
准确解释。此关键字的数据类型是了解其操作的最关键。 - Johnny Wong


答案:


语法是 * 和 **。名字 *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

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

>>> 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

正如您在本案中所看到的,它会获取项目的列表(或元组)并将其解包。通过它,它将它们与函数中的参数匹配。当然,你可以有一个 * 在函数定义和函数调用中都有。


1476
2017-08-03 08:38



你会在python的帮助/文档中看到这个怎么样? - Alex
@Alex: 这里 - Mr_and_Mrs_D
在函数调用的中间扩展作为位置参数传递的列表似乎是不可能的 function_call(arg1,arg2,*expanded_list_args,arg4,arg5)。扩展列表可能后面跟着我认为的关键字参数。有办法解决这个问题吗? - mlh3789
@ mlh3789是的,这只适用于python3。但真正有点奇怪的是:这种作品适用于作业: a, b, *c, d, e = 1, 2, 3, 4, 5, 6 将c赋予[3,4]。有点混乱 - Christian Tismer
在上面的例子中, mylist 就好像 an array 在JS中。我们的正常方法 print_three_things 需要3个参数。将其传递给print_three_things(*mylist), * 注释或多或少像 spreading operator 在ES6中。如果我的考虑是好还是错,请告诉我?谢谢 - Pristine Kallio


一个地方使用 *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类。


443
2017-08-03 08:39



如果您已经理解*和**,这个答案真的有意义。 - Scott David Tesler
我不明白。如果您的API发生了更改,您仍然需要更改实例化子类的所有位置。如果你要改变所有这些地方,那么你也可以修复子类的签名,而不是无缘无故地与args和kwargs一起攻击。你不需要了解Foo的理由是没有意义的,因为只要Foo构造函数的set签名发生变化,你所有的MyFoo实例化调用都必须改变。这需要了解Foo及其构造函数所需的参数。 - Zoran Pavlovic
@ZoranPavlovic可以使用此示例的示例是您要为现有产品提供附加组件并希望覆盖/扩展某些功能。通过使用* args和** kwargs,如果底层产品发生变化,这个附加组件将继续运行。但是,实例化MyFoo发生在加载项之外。这是否(更有意义)? (话虽如此:当你可以使用* args和** kwargs时,装饰器是一个更好的例子。) - Mark van Lent
你如何扩展这个子类的特定参数? - Hanan Shteingart
@HananShteingart例如这样:def __init __(self,foo,* args,** kwargs) - Mark van Lent


这是一个使用3种不同类型参数的示例。

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}

279
2017-08-03 08:42





这是我最喜欢的地方之一 ** 语法与Dave Webb的最后一个例子一样:

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

与仅使用名称本身相比,我不确定它是否非常快,但输入起来要容易得多!


65
2017-08-03 13:03



嘿,这家伙在很酷的时候发明了Python 3.6 f-strings - cat
@cat请解释一下?你指的是什么?没有 format 方法已经存在,甚至在Python 2中? - dabadaba
@dabadaba f-strings,而不是格式字符串(例如 f'{mystr} New-style formatting is {mynum}x more fun!') - Wayne Werner
@WayneWerner哦,我明白了,我不知道这个新的!谢谢 - dabadaba
@dabadaba恭喜成为今天的人之一 幸运的10k - Wayne Werner


* 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

39
2017-08-03 08:40





* args和** kwargs是Python的特殊魔术功能。 想想一个可能有未知数量的参数的函数。例如,无论出于何种原因,您希望具有对未知数量的数字求和的函数(并且您不想使用内置求和函数)。所以你写这个函数:

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

并使用它:sumFunction(3,4,6,3,6,8,9)。

** kwargs有不同的功能。使用** kwargs,您可以为函数提供任意关键字参数,并且可以作为dictonary访问它们。

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

调用someFunction(text =“foo”)将打印foo。


35
2017-08-03 08:40





想象一下你有一个功能,但你不想限制它所需的参数数量。 例:

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

然后你使用这个函数,如:

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

or

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

17
2017-08-03 08:40





名字 *args 和 **kwargs 要么 **kw 纯粹按照惯例。它使我们更容易阅读彼此的代码

一个方便的地方是使用struct模块

struct.unpack() 返回一个元组,而 struct.pack() 使用可变数量的参数。在操作数据时,能够将元组传递给它是很方便的 struck.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改变并且元组的大小发生变化,我将不得不返回并编辑那个真正的长行


14
2017-08-03 08:32