【问题标题】:What does ** (double star/asterisk) and * (star/asterisk) do for parameters?**(双星/星号)和*(星号/星号)对参数有什么作用?
【发布时间】:2021-09-21 22:17:43
【问题描述】:

在下面的方法定义中,***param2 做了什么?

def foo(param1, *param2):
def bar(param1, **param2):

【问题讨论】:

  • 这个问题是一个非常流行的重复目标,但不幸的是它经常被错误地使用。请记住,这个问题询问的是使用可变参数定义函数 (def func(*args))。有关询问函数 calls (func(*[1,2])) 中的含义的问题,请参阅 here。有关如何解压缩参数列表的问题,请参阅here。对于询问*literals ([*[1, 2]]) 中的含义的问题,请参阅here
  • @Aran-Fey:我认为“函数调用中的含义”的更好目标是What does the star operator mean, in a function call?Your link 并没有真正解决 ** 的使用问题,它是一个更狭窄的问题。

标签: python syntax parameter-passing variadic-functions argument-unpacking


【解决方案1】:

*args**kwargs 是一个常见的习惯用法,允许函数使用任意数量的参数,如 Python 文档中的 more on defining functions 部分所述。

*args 会给你所有的函数参数as a tuple:

def foo(*args):
    for a in args:
        print(a)        

foo(1)
# 1

foo(1,2,3)
# 1
# 2
# 3

**kwargs 会给你所有 关键字参数除了作为字典的形式参数对应的那些。

def bar(**kwargs):
    for a in kwargs:
        print(a, kwargs[a])  

bar(name='one', age=27)
# name one
# age 27

这两个习语都可以与普通参数混合,以允许一组固定和一些可变参数:

def foo(kind, *args, **kwargs):
   pass

也可以反过来使用:

def foo(a, b, c):
    print(a, b, c)

obj = {'b':10, 'c':'lee'}

foo(100,**obj)
# 100 10 lee

*l 习惯用法的另一种用法是在调用函数时解压缩参数列表

def foo(bar, lee):
    print(bar, lee)

l = [1,2]

foo(*l)
# 1 2

在 Python 3 中,可以在赋值 (Extended Iterable Unpacking) 的左侧使用 *l,尽管在这种情况下它给出的是列表而不是元组:

first, *rest = [1,2,3,4]
first, *l, last = [1,2,3,4]

Python 3 也增加了新的语义(参考PEP 3102):

def func(arg1, arg2, arg3, *, kwarg1, kwarg2):
    pass

此类函数仅接受 3 个位置参数,* 之后的所有内容都只能作为关键字参数传递。

注意:

  • Python dict,语义上用于关键字参数传递,是任意排序的。但是,在 Python 3.6 中,保证关键字参数会记住插入顺序。
  • **kwargs 中元素的顺序现在对应于传递给函数的关键字参数的顺序。” - What’s New In Python 3.6
  • 事实上,CPython 3.6 中的所有字典都会记住插入顺序作为实现细节,这在 Python 3.7 中成为标准。

【讨论】:

    【解决方案2】:

    另外值得注意的是,您也可以在调用函数时使用***。这是一个快捷方式,允许您使用列表/元组或字典直接将多个参数传递给函数。例如,如果您有以下功能:

    def foo(x,y,z):
        print("x=" + str(x))
        print("y=" + str(y))
        print("z=" + str(z))
    

    您可以执行以下操作:

    >>> mylist = [1,2,3]
    >>> foo(*mylist)
    x=1
    y=2
    z=3
    
    >>> mydict = {'x':1,'y':2,'z':3}
    >>> foo(**mydict)
    x=1
    y=2
    z=3
    
    >>> mytuple = (1, 2, 3)
    >>> foo(*mytuple)
    x=1
    y=2
    z=3
    

    注意:mydict 中的键名必须与函数foo 的参数完全相同。否则会抛出TypeError:

    >>> mydict = {'x':1,'y':2,'z':3,'badnews':9}
    >>> foo(**mydict)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: foo() got an unexpected keyword argument 'badnews'
    

    【讨论】:

      【解决方案3】:

      单个 * 表示可以有任意数量的额外位置参数。 foo() 可以像 foo(1,2,3,4,5) 一样调用。在 foo() 的主体中 param2 是一个包含 2-5 的序列。

      双 ** 表示可以有任意数量的额外命名参数。 bar() 可以像 bar(1, a=2, b=3) 一样调用。在 bar() 的主体中 param2 是一个包含 {'a':2, 'b':3 }

      的字典

      使用以下代码:

      def foo(param1, *param2):
          print(param1)
          print(param2)
      
      def bar(param1, **param2):
          print(param1)
          print(param2)
      
      foo(1,2,3,4,5)
      bar(1,a=2,b=3)
      

      输出是

      1
      (2, 3, 4, 5)
      1
      {'a': 2, 'b': 3}
      

      【讨论】:

        【解决方案4】:

        **(双星)和*(星)对参数有什么作用?

        它们允许定义函数以接受,并允许用户传递任意数量的参数、位置 (*) 和关键字 (**)。

        定义函数

        *args 允许任意数量的可选位置参数(参数),这些参数将分配给名为 args 的元组。

        **kwargs 允许任意数量的可选关键字参数(参数),它们将位于名为 kwargs 的字典中。

        您可以(并且应该)选择任何合适的名称,但如果意图使参数具有非特定语义,argskwargs 是标准名称。

        扩展,传递任意数量的参数

        您还可以使用*args**kwargs 分别从列表(或任何可迭代)和字典(或任何映射)传入参数。

        接收参数的函数不必知道它们正在被扩展。

        例如,Python 2 的 xrange 没有明确地期望 *args,但因为它需要 3 个整数作为参数:

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

        Python 3 中的新功能:使用仅关键字参数定义函数

        您可以在*args 之后添加keyword only arguments - 例如,这里必须将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'
        

        Python 2 兼容演示

        *args(通常表示“star-args”)和**kwargs(可以通过说“kwargs”来暗示星号,但要明确表示“double-star kwargs”)是 Python 使用 @ 的常见习语987654356@ 和 ** 表示法。这些特定的变量名不是必需的(例如,您可以使用 *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 允许我们这样做),我们减少了代码行数,提高了可读性和可维护性,并为我们的程序中的逻辑提供了唯一的规范位置。如果我们需要更改此结构的任何部分,我们可以在一个地方进行每次更改。

        【讨论】:

          【解决方案5】:

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

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

          所以我们现在知道了带有位置参数和关键字参数的函数定义。

          现在让我们研究一下'*'运算符和'**'运算符。

          请注意,这些运算符可用于 2 个领域:

          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
          

          definition函数中,'*'操作符将接收到的参数打包成一个元组。

          现在让我们看一个在函数定义中使用'**'的例子:

          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
          

          在函数定义中,'**'操作符将接收到的参数打包到字典中。

          请记住:

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

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

          函数定义中,'*'位置参数打包到一个元组中。

          函数定义中,'**'关键字参数打包到字典中。

          【讨论】:

            【解决方案6】:

            此表便于在函数构造和函数调用中使用***

                        In function construction         In function call
            =======================================================================
                      |  def f(*args):                 |  def f(a, b):
            *args     |      for arg in args:          |      return a + b
                      |          print(arg)            |  args = (1, 2)
                      |  f(1, 2)                       |  f(*args)
            ----------|--------------------------------|---------------------------
                      |  def f(a, b):                  |  def f(a, b):
            **kwargs  |      return a + b              |      return a + b
                      |  def g(**kwargs):              |  kwargs = dict(a=1, b=2)
                      |      return f(**kwargs)        |  f(**kwargs)
                      |  g(a=1, b=2)                   |
            -----------------------------------------------------------------------
            

            这实际上只是对 Lorin Hochstein 的 answer 的总结,但我觉得它很有帮助。

            相关:star/splat 运算符在 Python 3 中的用途是 expanded

            【讨论】:

              【解决方案7】:

              *** 在函数参数列表中有特殊用法。 * 暗示参数是一个列表,** 暗示参数 是一本字典。这允许函数采用任意数量的 论据

              【讨论】:

                【解决方案8】:

                献给那些通过例子学习的人!

                1. * 的目的是让您能够定义一个函数,该函数可以采用列表形式提供的任意数量的参数(例如 f(*myList))。
                2. ** 的目的是让您能够通过提供字典(例如 f(**{'x' : 1, 'y' : 2}))来提供函数的参数。

                让我们通过定义一个函数来展示这一点,该函数接受两个普通变量xy,并且可以接受更多参数为myArgs,并且可以接受更多参数为myKW。稍后,我们将展示如何使用myArgDicty

                def f(x, y, *myArgs, **myKW):
                    print("# x      = {}".format(x))
                    print("# y      = {}".format(y))
                    print("# myArgs = {}".format(myArgs))
                    print("# myKW   = {}".format(myKW))
                    print("# ----------------------------------------------------------------------")
                
                # Define a list for demonstration purposes
                myList    = ["Left", "Right", "Up", "Down"]
                # Define a dictionary for demonstration purposes
                myDict    = {"Wubba": "lubba", "Dub": "dub"}
                # Define a dictionary to feed y
                myArgDict = {'y': "Why?", 'y0': "Why not?", "q": "Here is a cue!"}
                
                # The 1st elem of myList feeds y
                f("myEx", *myList, **myDict)
                # x      = myEx
                # y      = Left
                # myArgs = ('Right', 'Up', 'Down')
                # myKW   = {'Wubba': 'lubba', 'Dub': 'dub'}
                # ----------------------------------------------------------------------
                
                # y is matched and fed first
                # The rest of myArgDict becomes additional arguments feeding myKW
                f("myEx", **myArgDict)
                # x      = myEx
                # y      = Why?
                # myArgs = ()
                # myKW   = {'y0': 'Why not?', 'q': 'Here is a cue!'}
                # ----------------------------------------------------------------------
                
                # The rest of myArgDict becomes additional arguments feeding myArgs
                f("myEx", *myArgDict)
                # x      = myEx
                # y      = y
                # myArgs = ('y0', 'q')
                # myKW   = {}
                # ----------------------------------------------------------------------
                
                # Feed extra arguments manually and append even more from my list
                f("myEx", 4, 42, 420, *myList, *myDict, **myDict)
                # x      = myEx
                # y      = 4
                # myArgs = (42, 420, 'Left', 'Right', 'Up', 'Down', 'Wubba', 'Dub')
                # myKW   = {'Wubba': 'lubba', 'Dub': 'dub'}
                # ----------------------------------------------------------------------
                
                # Without the stars, the entire provided list and dict become x, and y:
                f(myList, myDict)
                # x      = ['Left', 'Right', 'Up', 'Down']
                # y      = {'Wubba': 'lubba', 'Dub': 'dub'}
                # myArgs = ()
                # myKW   = {}
                # ----------------------------------------------------------------------
                

                注意事项

                1. ** 专供字典使用。
                2. 非可选参数分配首先发生。
                3. 您不能两次使用非可选参数。
                4. 如果适用,** 必须始终位于 * 之后。

                【讨论】:

                  【解决方案9】:

                  TL;DR

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

                  1. 要使用*args 接受任意数量的位置参数: def foo(*args): pass,这里foo 接受任意数量的位置参数,即。即,以下调用有效foo(1)foo(1, 'bar')
                  2. 要使用**kwargs 接受任意数量的关键字参数: def foo(**kwargs): pass,这里 'foo' 接受任意数量的关键字参数,即。例如,以下调用有效foo(name='Tom')foo(name='Tom', age=33)
                  3. 要使用*args, **kwargs 接受任意数量的位置和关键字参数: def foo(*args, **kwargs): pass,这里foo 接受任意数量的位置和关键字参数,即。即,以下调用有效foo(1,name='Tom')foo(1, 'bar', name='Tom', age=33)
                  4. 使用* 强制仅使用关键字参数: def foo(pos1, pos2, *, kwarg1): pass,这里* 表示 foo 只接受 pos2 之后的关键字参数,因此 foo(1, 2, 3) 引发 TypeError 但 foo(1, 2, kwarg1=3) 可以。李>
                  5. 使用*_ 表示对更多位置参数不再感兴趣(注意:这只是一个约定): def foo(bar, baz, *_): pass 表示(按照约定)foo 仅使用barbaz在其工作中的争论,并会忽略其他人。
                  6. 使用\**_ 表示对更多关键字参数不再感兴趣(注意:这只是一个约定): def foo(bar, baz, **_): pass 表示(按照约定)foo 仅使用barbaz在其工作中的争论,并会忽略其他人。

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

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

                  【讨论】:

                    【解决方案10】:

                    来自 Python 文档:

                    如果位置参数的数量多于形参槽的数量,则会引发 TypeError 异常,除非存在使用语法“*identifier”的形参;在这种情况下,该形式参数接收一个包含多余位置参数的元组(如果没有多余的位置参数,则接收一个空元组)。

                    如果任何关键字参数不对应于形式参数名称,则会引发 TypeError 异常,除非存在使用语法“**identifier”的形式参数;在这种情况下,该形式参数接收一个包含多余关键字参数的字典(使用关键字作为键,参数值作为对应值),或者如果没有多余的关键字参数,则接收一个(新)空字典。

                    【讨论】:

                      【解决方案11】:

                      * 表示以元组形式接收变量参数

                      ** 表示接收变量参数作为字典

                      使用如下:

                      1) 单人 *

                      def foo(*args):
                          for arg in args:
                              print(arg)
                      
                      foo("two", 3)
                      

                      输出:

                      two
                      3
                      

                      2) 现在**

                      def bar(**kwargs):
                          for key in kwargs:
                              print(key, kwargs[key])
                      
                      bar(dic1="two", dic2=3)
                      

                      输出:

                      dic1 two
                      dic2 3
                      

                      【讨论】:

                        【解决方案12】:

                        在 Python 3.5 中,您还可以在 listdicttupleset 显示(有时也称为文字)中使用此语法。见PEP 488: Additional Unpacking Generalizations

                        >>> (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 链接。)

                        【讨论】:

                        • 我不确定这是否违反了“只有一种方法可以做到”。没有其他方法可以从多个可迭代对象中初始化列表/元组——您目前需要将它们链接到单个可迭代对象中,这并不总是很方便。您可以在PEP-0448 中阅读有关理性的信息。此外,这不是 python3.x 功能,它是 python3.5+ 功能:-)。
                        【解决方案13】:

                        我想举一个别人没有提到的例子

                        * 也可以解压一个generator

                        Python3 文档中的示例

                        x = [1, 2, 3]
                        y = [4, 5, 6]
                        
                        unzip_x, unzip_y = zip(*zip(x, y))
                        

                        unzip_x 将是 [1, 2, 3],unzip_y 将是 [4, 5, 6]

                        zip() 接收多个 iretable args,并返回一个生成器。

                        zip(*zip(x,y)) -> zip((1, 4), (2, 5), (3, 6))
                        

                        【讨论】:

                        • unzip_x 将是 (1, 2, 3) 而不是 [1, 2, 3]。 unzip_y 也是如此
                        【解决方案14】:

                        基于 nickd 的 answer...

                        def foo(param1, *param2):
                            print(param1)
                            print(param2)
                        
                        
                        def bar(param1, **param2):
                            print(param1)
                            print(param2)
                        
                        
                        def three_params(param1, *param2, **param3):
                            print(param1)
                            print(param2)
                            print(param3)
                        
                        
                        foo(1, 2, 3, 4, 5)
                        print("\n")
                        bar(1, a=2, b=3)
                        print("\n")
                        three_params(1, 2, 3, 4, s=5)
                        

                        输出:

                        1
                        (2, 3, 4, 5)
                        
                        1
                        {'a': 2, 'b': 3}
                        
                        1
                        (2, 3, 4)
                        {'s': 5}
                        

                        基本上,任意数量的位置参数都可以使用*args,任何命名参数(或kwargs aka关键字参数)都可以使用**kwargs。

                        【讨论】:

                          【解决方案15】:

                          TL;DR

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

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

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

                          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,这是一个 dict,其值为 {"keyword" : "ligma", "options" : [1,2,3]}


                          这也适用于反向,即从调用方。例如,如果您有一个函数定义为:

                          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)
                          

                          【讨论】:

                            【解决方案16】:

                            除了函数调用之外,*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')
                            

                            完整代码为here

                            【讨论】:

                              【解决方案17】:

                              *args**kwargs:允许您将可变数量的参数传递给函数。

                              *args: 用于向函数发送非关键字变长参数列表:

                              def args(normal_arg, *argv):
                                  print("normal argument:", normal_arg)
                              
                                  for arg in argv:
                                      print("Argument in list of arguments from *argv:", arg)
                              
                              args('animals', 'fish', 'duck', 'bird')
                              

                              将产生:

                              normal argument: animals
                              Argument in list of arguments from *argv: fish
                              Argument in list of arguments from *argv: duck
                              Argument in list of arguments from *argv: bird
                              

                              **kwargs*

                              **kwargs 允许您将关键字可变长度的参数传递给函数。如果你想在函数中处理命名参数,你应该使用**kwargs

                              def who(**kwargs):
                                  if kwargs is not None:
                                      for key, value in kwargs.items():
                                          print("Your %s is %s." % (key, value))
                              
                              who(name="Nikola", last_name="Tesla", birthday="7.10.1856", birthplace="Croatia")  
                              

                              将产生:

                              Your name is Nikola.
                              Your last_name is Tesla.
                              Your birthday is 7.10.1856.
                              Your birthplace is Croatia.
                              

                              【讨论】:

                                【解决方案18】:

                                给定一个有 3 个项目作为参数的函数

                                sum = lambda x, y, z: x + y + z
                                sum(1,2,3) # sum 3 items
                                
                                sum([1,2,3]) # error, needs 3 items, not 1 list
                                
                                x = [1,2,3][0]
                                y = [1,2,3][1]
                                z = [1,2,3][2]
                                sum(x,y,z) # ok
                                
                                sum(*[1,2,3]) # ok, 1 list becomes 3 items
                                

                                想象一下这个玩具有一袋三角形、一个圆形和一个矩形物品。那个包不直接适合。您需要打开袋子才能拿走这 3 件物品,现在它们都装好了。 Python * 运算符执行此解包过程。

                                【讨论】:

                                  【解决方案19】:

                                  在函数中同时使用两者的一个很好的例子是:

                                  >>> def foo(*arg,**kwargs):
                                  ...     print arg
                                  ...     print kwargs
                                  >>>
                                  >>> a = (1, 2, 3)
                                  >>> b = {'aa': 11, 'bb': 22}
                                  >>>
                                  >>>
                                  >>> foo(*a,**b)
                                  (1, 2, 3)
                                  {'aa': 11, 'bb': 22}
                                  >>>
                                  >>>
                                  >>> foo(a,**b) 
                                  ((1, 2, 3),)
                                  {'aa': 11, 'bb': 22}
                                  >>>
                                  >>>
                                  >>> foo(a,b) 
                                  ((1, 2, 3), {'aa': 11, 'bb': 22})
                                  {}
                                  >>>
                                  >>>
                                  >>> foo(a,*b)
                                  ((1, 2, 3), 'aa', 'bb')
                                  {}
                                  

                                  【讨论】:

                                    【解决方案20】:

                                    这个示例将帮助您记住*args**kwargs 甚至super 以及Python 中的继承。

                                    class base(object):
                                        def __init__(self, base_param):
                                            self.base_param = base_param
                                    
                                    
                                    class child1(base): # inherited from base class
                                        def __init__(self, child_param, *args) # *args for non-keyword args
                                            self.child_param = child_param
                                            super(child1, self).__init__(*args) # call __init__ of the base class and initialize it with a NON-KEYWORD arg
                                    
                                    class child2(base):
                                        def __init__(self, child_param, **kwargs):
                                            self.child_param = child_param
                                            super(child2, self).__init__(**kwargs) # call __init__ of the base class and initialize it with a KEYWORD arg
                                    
                                    c1 = child1(1,0)
                                    c2 = child2(1,base_param=0)
                                    print c1.base_param # 0
                                    print c1.child_param # 1
                                    print c2.base_param # 0
                                    print c2.child_param # 1
                                    

                                    【讨论】:

                                      【解决方案21】:

                                      上下文

                                      • python 3.x
                                      • **解包
                                      • 与字符串格式一起使用

                                      与字符串格式一起使用

                                      除了这个帖子中的答案之外,还有其他地方没有提到的另一个细节。这扩展了answer by Brad Solomon

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

                                      这有点类似于您可以使用 python f-strings 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])
                                      
                                      

                                      【讨论】:

                                        【解决方案22】:

                                        *args ( or *any ) 表示每个参数

                                        def any_param(*param):
                                            pass
                                        
                                        any_param(1)
                                        any_param(1,1)
                                        any_param(1,1,1)
                                        any_param(1,...)
                                        

                                        注意:您不能将参数传递给 *args

                                        def any_param(*param):
                                            pass
                                        
                                        any_param() # will work correct
                                        

                                        *args 是元组类型

                                        def any_param(*param):
                                            return type(param)
                                        
                                        any_param(1) #tuple
                                        any_param() # tuple
                                        

                                        为了访问元素,不要使用 *

                                        def any(*param):
                                            param[0] # correct
                                        
                                        def any(*param):
                                            *param[0] # incorrect
                                        

                                        **kwd

                                        **kwd 或 **any 这是一个dict类型

                                        def func(**any):
                                            return type(any) # dict
                                        
                                        def func(**any):
                                            return any
                                        
                                        func(width="10",height="20") # {width="10",height="20")
                                        
                                        
                                        

                                        【讨论】:

                                          【解决方案23】:
                                          • def foo(param1, *param2): 是一种可以为*param2 接受任意数量值的方法,
                                          • def bar(param1, **param2): 是一种可以接受任意数量的值的方法,带有 *param2 的键
                                          • param1 是一个简单的参数。

                                          例如,在Java中实现varargs的语法如下:

                                          accessModifier methodName(datatype… arg) {
                                              // method body
                                          }
                                          

                                          【讨论】:

                                            猜你喜欢
                                            相关资源
                                            最近更新 更多