【问题标题】:Confusion with Python functions using an argument, keyword argument, *args, **kwargs使用参数、关键字参数、*args、**kwargs 与 Python 函数混淆
【发布时间】:2023-04-06 19:00:01
【问题描述】:

鉴于以下函数和对print_stuff() 的调用,有人可以解释为什么在没有关键字 arg default 但将列表传递给*args 的情况下调用函数时会出现意外行为吗?

我知道涉及可变/不可变关键字默认值的“陷阱”,但我认为这与此处无关。

有人可以澄清为什么会发生这种情况,或者任何语法/调用错误吗?

def print_stuff(arg, kwarg=None, *args):
    print "arg: %s" % arg

    if kwarg:
        print "kwarg: %s" % kwarg

    if args:
        for a in args:
            print "printing {} from args".format(a)

    print "===end==="

args_list = range(1, 3)
kwargs_list = {str(a):a for a in args_list}

print_stuff('hi', kwarg='some_kwarg') # works as intended

print_stuff('hi', 'some_kwarg', *range(1, 3)) # also works as intended

print_stuff('hi', *range(1, 3)) # first element of list unexpectedly passed in to keyword argument, even using the * notation

print_stuff('hi', kwarg='some_kwarg', *range(1, 3)) # TypeError: print_stuff() got multiple values for keyword argument 'kwarg'

【问题讨论】:

  • 您没有任何关键字-only 参数,因此您传入的第二个参数将分配给kwarg
  • 第二个值作为第二个参数传入有什么意外?

标签: python args keyword-argument


【解决方案1】:

除了kwarg 有一个默认值之外,print_stuff(arg, kwarg=None, *args)print_stuff(arg, kwarg, *args) 没有什么不同 - 如果有第二个位置参数,它作为 kwarg 参数传递(任何后续位置参数都以args)。

请注意:

print_stuff('hi', *range(1, 3))

被评估为:

print_stuff('hi', 1, 2)

所以第一个参数转到arg,第二个转到kwarg,第三个转到args[0]


如果我们注意到:

print_stuff('hi', kwarg='some_kwarg', *range(1, 3))

相当于:

print_stuff('hi', *range(1, 3), kwarg='some_kwarg')

因此:

print_stuff('hi', 1, 2, kwarg='some_kwarg')

您也许可以看到问题 - 再次 'hi'12 分别转到 argkwargargs[0],然后是 @987654341 的 另一个 值@意外出现。


如果您希望在考虑kwarg 之前“吸收”所有位置参数,请将函数定义更改为:

def print_stuff(arg, *args, **kwargs):
    print "arg: %s" % arg
    kwarg = kwargs.get('kwarg')
    if kwarg is not None:  # note explicit test - what if kwarg == 0?
        print "kwarg: %s" % kwarg

    for a in args:  # no need to test here - loop doesn't run if no args
        print "printing {} from args".format(a)

    print "===end==="

使用中:

>>> print_stuff('hi', *range(1, 3))
arg: hi
printing 1 from args
printing 2 from args
===end===
>>> print_stuff('hi', *range(1, 3), kwarg='some_kwarg')
arg: hi
kwarg: some_kwarg
printing 1 from args
printing 2 from args
===end===

请注意,在 3.x 中,您可以使用 keyword-only arguments,即以下是替代方案:

def print_stuff(arg, *args, kwarg=None):
    ...

【讨论】:

  • 谢谢,选择这个是因为它提供了解释+解决方案。
【解决方案2】:

kwarg 不是仅关键字参数;它只是一个具有默认值的位置参数。您的来电

print_stuff('hi', *range(1,3))

完全一样

print_stuff('hi', 1, 2)

它将前两个参数分配给前两个命名参数,其余的(即第三个)放在*args参数中。

【讨论】:

    【解决方案3】:

    一种简单的思考方式是:

    当一个参数被传递给一个函数时,它会被添加到一个位置参数“队列”中。分配给此队列时,Python 将忽略关键字参数并优先考虑非关键字参数。所有关键字参数都在函数调用中分配last。您可以将其想象为 Python “围绕您的参数的顺序移动”,因为它渴望首先填补职位。所以像这样的电话:

    print_stuff('hi', kwarg='some_kwarg', *range(1,3))

    Python 基本上会把它变成:

    print_stuff('hi', 1, 2, kwarg='some_kwarg')

    然后会因为你分配给 kwarg 两次而生气。

    ***请注意,这不一定是实际发生的,但这是一种很好的思考方式,因为您将能够处理错误并且还可以在如下调用中解释原因:

    print_stuff('hi', *range(1,3))

    1 被传递给第二个位置参数 kwarg,2 被传递给第三个参数 args。

    【讨论】:

    • 谢谢,将其视为在参数队列中移动很有用。
    【解决方案4】:

    我只是在编写一个函数来展示*args**kwargs 的工作原理

    def test_function(normal_arg, *arg, **kwargs):
        print(locals())
        return
    
    test_function("normal_arg-value", 
        "*arg1-value", "*arg2-value",
        karg1="karg1-value", karg2="karg2-value")
    
    #output
    {'arg1': 'arg1-value', 'arg': ('*arg1-value', '*arg2-value'), 'kwargs': {'karg2': 'karg2-value', 'karg1': 'karg1-value'}}
    

    希望这会有所帮助。

    如何将无限的参数传递给函数

    def test(*lis):
        print locals()
    
    test(1,2,3)
    
    #output
    {'lis': (1, 2, 3)}
    

    无限的命名参数如何传递给函数

    def test(**dicts):
        print locals()
    
    test(arg1="value1", arg2="value2")
    
    #output
    {'dicts': {'arg1': 'value1', 'arg2': 'value2'}}
    

    字典如何作为参数传递给函数

    def test(**dicts):
        print locals()
    
    test(**{"arg1":"value1", "arg2": "value2"})
    
    #output
    {'dicts': {'arg1': 'value1', 'arg2': 'value2'}}
    

    希望对你有帮助

    【讨论】:

      猜你喜欢
      • 2012-12-09
      • 1970-01-01
      • 1970-01-01
      • 2022-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-09
      • 1970-01-01
      相关资源
      最近更新 更多