为什么* args参数解包会产生一个元组?

时间:2019-01-13 16:11:26

标签: python function arguments

在python中,可以定义一个采用任意数量的位置参数的函数,如下所示:

def f(*args):
    print(args)
f(1, 2, 3)  # (1, 2, 3)

当称为f(a, b, c)时,所有位置参数都组合成一个 tuple 。 python 23文档中对此行为进行了描述,但是我还没有找到PEP。

PEP 3132,在“接受”条件下引入了扩展的可迭代拆包(first, *middle, last = seqence)状态

  

将加星标的目标设为元组而不是列表。这将与函数的* args一致,但会使结果的进一步处理变得更加困难。

被讨论。如果我写一个包装器,我可能还想进一步处理这样的参数:

def force_type(position, type):
    def wrapper(f):
        def new(*args, **kwargs):
            args = list(args)  # Why?
            args[position] = type(args[position])
            return f(*args, **kwargs)
        return new
    return wrapper

@force_type(1, int)
def func(a, b, c):
    assert isinstance(b, int)

argstuple的事实使进一步的处理变得更加困难。只是在引入包装的早期阶段不使用包装纸吗?如果是这样,为什么在python3中没有其他兼容性破坏性更改(PEP3132倾向于简化处理而不是一致性(这似乎至少类似于兼容性破坏性更改中的兼容性),所以这种变化)。

为什么功能*args(仍然)还是tuple,即使list允许更轻松地进行进一步处理?

3 个答案:

答案 0 :(得分:1)

为什么不呢?关于元组的事情是,创建后不能更改它。这样可以提高执行脚本的速度,并且您实际上不需要功能参数列表,因为您实际上不需要修改函数的给定参数。 您是否需要为参数添加或删除方法?大多数情况下不会。您是否希望程序运行速度更快。那是肯定的。这就是大多数人更喜欢拥有东西的方式。 * args之所以返回元组,是因为如果您确实需要一个列表,则可以用一行代码对其进行转换!

args = list(args)

因此,通常: 它可以加快程序执行速度。您不必更改参数。更改其类型并不难。

答案 1 :(得分:1)

我不知道这是否是其背后的想法,但是这种易于处理的方式(即使用list数据实例化一个tuple也不是那么难)会导致混乱的行为

def fce1(*args):
   fce2(args)
   # some more code using args

def fce2(args):
   args.insert(0, 'other_val')

fce1(1, 2, 3)

编写fce1代码的人如果没有意识到后来要处理的args不是该函数所调用的内容,可能会感到惊讶。

我还假定不可变类型在内部更易于处理,并且开销较小。

答案 2 :(得分:1)

我的最佳猜测是,如果* args生成一个list(可变),则在多种情况下都可能导致非常令人惊讶的结果。 @Ondrej K.举了一个很好的例子。打个比方,当将列表作为默认参数时,每个函数调用都可能具有不同的默认参数。这是默认参数仅被评估一次的结果,这种情况并不是最直观的。甚至官方的python文档也针对这种情况提供了一种特定的解决方法。

执行功能定义时,从左到右评估默认参数值。这意味着在定义函数时,表达式将被计算一次,并且每次调用均使用相同的“预计算”值。这对于理解默认参数何时是可变对象(例如列表或字典)尤其重要:如果函数修改了该对象(例如,通过将项目附加到列表),则默认值实际上已被修改。这通常不是预期的。解决此问题的一种方法是使用None作为默认值,并在函数主体中明确测试它,例如:

def whats_on_the_telly(penguin=None):
if penguin is None:
    penguin = []
penguin.append("property of the zoo")
return penguin

Source documentation

总而言之,我认为* args是元组,因为将其作为列表会引起与可变类型相关的所有问题(例如速度较慢),而更大的问题是大多数人不希望函数参数发生变化。 尽管我确实同意此实现与PEP-3132非常不一致,并且会引起大多数学习者的困惑。我刚接触Python,花了一段时间才明白* args是元组而不是列表的原因,以便与PEP-3132's接受保持一致。