为什么使用压缩的*args/**kwargs而不是传递list/didict



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

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有优势吗?或者在某些情况下一个更合适?

*args/**kwargs有其优点,通常是在您希望能够传入未打包的数据结构,同时保留使用打包数据结构的能力的情况下。Python3的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()那样被传递数据结构中包含的多个参数,那么省略*语法可能更有意义。

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

理论上,您也可以接受值设置为None的字典。为什么不呢?这是开销,没有必要。对我来说,在可以接受varargs的情况下接受列表会增加开销。(正如其中一条评论所指出的)

此外,varargs是保证调用方和被调用函数之间一致性和良好契约的好方法。无法做出任何假设。

如果你需要一份清单,那么你就知道你需要一个清单!

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

def f(l = []): pass

很酷,现在您有两个问题,因为您必须确保不修改参数l:默认参数值。为什么?因为你不喜欢*args.:)

附言:我认为这是动态语言最大的缺点之一:你看不到界面了,但是的!有一个接口!

这是一个旧的例子,但为了回答@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语法,它不局限于特定的函数。

最新更新