【问题标题】:Why use packed *args/**kwargs instead of passing list/dict?为什么使用打包的 *args/**kwargs 而不是传递 list/dict?
【发布时间】:2016-02-06 04:38:19
【问题描述】:

如果我不知道一个函数将传递多少个参数,我可以使用参数打包来编写函数:

def add(factor, *nums):
    """Add numbers and multiply by factor."""
    return sum(nums) * factor

或者,我可以通过传递一个数字列表作为参数来避免参数打包:

def add(factor, nums):
    """Add numbers and multiply by factor.

    :type factor: int
    :type nums: list of int
    """
    return sum(nums) * factor

使用参数包装*args 比传递数字列表有优势吗?还是有更合适的情况?

【问题讨论】:

  • 这样您就不必首先传入一个结构。
  • 观察调用语法。选择你喜欢的任务。
  • @IgnacioVazquez-Abrams 这样做有什么好处?将括号括在参数周围同样容易。
  • @NewWorld 如果您愿意,请执行此操作。在这种情况下,我认为后者更清楚,但YMMV。 有什么问题?
  • @NewWorld 在一般情况下很难回答 - 在您的示例中,没有任何好处。当您遇到有帮助的案例时,您就会知道!例如,当您使用继承做一些复杂的事情时,您可以使用*args**kwargs 来消除 MRO 中不同类之间的接口差异。

标签: python parameter-passing keyword-argument


【解决方案1】:

这与 API 有关:*args 提供了更好的接口,因为它声明该方法接受任意数量的参数,仅此而已 - 没有进一步的假设。您肯定知道该方法本身不会对包含各种参数的数据结构做任何其他事情,并且不需要特殊的数据结构。

理论上,您也可以接受值设置为 None 的字典。为什么不?这是开销和不必要的。对我来说,在可以接受可变参数时接受列表会增加开销。 (正如其中一位 cmets 指出的那样)

此外,可变参数是保证调用者和被调用函数之间的一致性和良好契约的好方法。不能做任何假设。

当你需要一个列表时,你就知道你需要一个列表!

啊,请注意 f(*args) 与 f(list) 不同:第二个想要列表,第一个接受任意数量的参数(包括 0)。好的,让我们将第二个定义为可选参数:

def f(l = []): pass

酷,现在你有两个问题,因为你必须确保你没有修改参数 l:default parameter values。是什么原因?因为你不喜欢 *args。 :)

PS:我认为这是动态语言的最大缺点之一:您不再看到界面,但是是的!有界面!

【讨论】:

  • 最后一个例子有点做作,因为它通常用def f(l=None): l = l or []解决...
  • 当您可以使用 *args 时,您为什么要这样做?这才是重点。我提供了一个错误的函数签名,我还必须修复它吗?这是修复设计问题的样板代码。
  • 我认为这些问题无关。如果您想提供可变函数签名,*args 是最直接的方法。如果您想提供单参数作为列表的签名,None 是这样做的方法。您不会通过使用完全不同的签名 f(*args) 来修复带有签名 f(l: list=None) 的函数。您希望您的 API 看起来像什么是第一位的,其次是如何最好地实现它。
  • 这正是我的答案(前几行)。一切都与您想要提供的 API 和接口有关。样板代码不能解决设计问题。实际上,这让情况变得更糟。
【解决方案2】:

*args/**kwargs 有其优势,通常在您希望能够传入未打包的数据结构的情况下,同时保留使用打包数据结构的能力。 Python 3 的 print() 就是一个很好的例子。

print('hi')
print('you have', num, 'potatoes')
print(*mylist)

print() 仅采用压缩结构然后在函数内扩展它的情况形成对比:

print(('hi',))
print(('you have', num, 'potatoes'))
print(mylist)

在这种情况下,*args/**kwargs 就派上用场了。

当然,如果您希望函数始终传递包含在数据结构中的多个参数,就像 sum()str.join() 所做的那样,那么省略 * 语法可能更有意义。

【讨论】:

    【解决方案3】:

    这有点老了,但要回答@DBrenko 关于何时需要 *args 和 **kwargs 的查询,我发现的最清楚的例子是当你想要一个运行另一个函数作为其执行的一部分的函数时.举个简单的例子:

    import datetime
    
    def timeFunction(function, *args, **kwargs):
        start = datetime.datetime.now()
        output = function(*args, **kwargs)
        executionTime = datetime.datetime.now() - start
        return executionTime, output 
    

    timeFunction 将一个函数和该函数的参数作为其参数。通过使用 *args, **kwargs 语法,它不限于特定的功能。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-26
      • 2016-09-03
      • 1970-01-01
      • 1970-01-01
      • 2021-03-02
      • 2015-09-02
      • 1970-01-01
      相关资源
      最近更新 更多