Python 为什么*args参数解包会给出一个元组?

Python 为什么*args参数解包会给出一个元组?,python,function,arguments,Python,Function,Arguments,在python中,可以使用任意数量的位置参数定义函数,如下所示: def f(*args): print(args) f(1, 2, 3) # (1, 2, 3) def force_type(position, type): def wrapper(f): def new(*args, **kwargs): args = list(args) # Why? args[position] = type(args

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

def f(*args):
    print(args)
f(1, 2, 3)  # (1, 2, 3)
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)
当被称为
f(a,b,c)
时,所有位置参数都放在一个元组中。 python和文档中描述了这种行为,但我还没有找到它的PEP

,引入扩展的iterable解包(
first,*middle,last=sequence
)在“Acceptance”下声明

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

进行了讨论。如果我写了一个包装器,我可能还想进一步处理如下参数:

def f(*args):
    print(args)
f(1, 2, 3)  # (1, 2, 3)
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)
由于
args
是一个
tuple
,这使得进一步的处理变得更加困难。包装纸是不是在引入的早期阶段就没有使用?如果是这样的话,为什么python3中没有改变这一点以及其他破坏兼容性的更改(PEP3132更倾向于易于处理而不是一致性(这至少与破坏兼容性的更改中的兼容性类似)


为什么函数是args(仍然)
元组
即使
列表
允许更简单的进一步处理?

为什么不呢?元组的问题是,创建后您不能更改它。这可以提高脚本的执行速度,并且您不需要函数参数的列表,因为您实际上不需要修改给定的参数函数的n个参数。 您需要为参数添加或删除方法吗?大多数情况下都是否。您希望程序运行得更快吗?是的。大多数人都希望这样。因为*args东西会返回tuple,如果您确实需要一个列表,您可以用一行代码转换它

args = list(args)
因此,一般来说:
它加快了程序的执行速度。您不需要更改参数。更改它的类型并不是那么难。

我不知道这是否是它背后的想法,但是处理的简单性(即使用
元组
数据实例化
列表
并不是那么难)可能会导致混乱的行为

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

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

fce1(1, 2, 3)
编写
fce1
代码的人可能会感到惊讶,因为他们没有意识到他们以后处理的
args
不是函数调用的对象


我还假设不可变类型更容易在内部处理,并且开销更少。

我最好的猜测是如果*args生成一个列表(可变),它可以在多种情况下产生非常令人惊讶的结果。@Ondrej K.给出了一个很好的例子。作为类比,当列表作为默认参数时,每个函数调用可能都有不同的默认参数。这是默认参数只计算一次的结果,这种情况并不是最直观的。即使官方python文档针对这种情况有一个特定的解决方法

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

总而言之,我认为*args是一个元组,因为将它作为一个列表会导致与可变类型相关的所有问题(比如速度较慢),而更大的问题是大多数函数参数都不会改变。
虽然我同意这种实现与大多数学习者非常不一致,并且会造成混乱。我对Python非常陌生,为了与接受一致,我花了一段时间才理解*args是元组而不是列表的原因。

通常不需要可变的
args
。元组是创建比列表更快,占用的内存更少。@timgeb所以这只是一个性能增益,因为
args
处理比赋值要少得多?我不知道,但这是我最好的猜测。