简体   繁体   English

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

[英]Why does *args argument unpacking give a tuple?

In python, it is possible to define a function taking an arbitrary number of positional arguments like so:在 python 中,可以定义一个带有任意数量的位置参数的函数,如下所示:

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

When called as f(a, b, c) , all positional arguments are put together into a tuple .当被调用为f(a, b, c) ,所有位置参数都放在一个tuple 中 This behavior is described in python 2 and 3 documentation, but I haven't found a PEP to it.这种行为在 python 23文档中有所描述,但我还没有找到它的 PEP。

PEP 3132 , introducing extended iterable unpacking ( first, *middle, last = seqence ) states under "Acceptance" that PEP 3132 ,在“接受”下引入扩展的可迭代解包( first, *middle, last = seqence )状态

Make the starred target a tuple instead of a list.使加星标的目标成为元组而不是列表。 This would be consistent with a function's *args, but make further processing of the result harder.这将与函数的 *args 一致,但会使结果的进一步处理更加困难。

was discussed.进行了讨论。 If I write a wrapper, I may also want to further process arguments like so:如果我写了一个包装器,我可能还想进一步处理这样的参数:

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)

This further processing is made harder by the fact args is a tuple .由于args是一个tuple这个进一步的处理变得更加困难。 Were wrappers just not used at the early stages this was introduced?在引入的早期阶段是否没有使用包装器? If so, why wasn't this changed in python3 with other compatibility breaking changes (PEP3132 favours ease of processing over consistency (which seems at least similar to compatibility in a compatibility- breaking change).如果是这样,为什么这在 python3 中没有通过其他兼容性破坏性更改进行更改(PEP3132 更倾向于易于处理而不是一致性(这似乎至少类似于兼容性破坏性更改中的兼容性)。

Why are a functions *args (still) a tuple even though a list allows easier further processing?为什么一个函数*args (仍然)是一个tuple即使list允许更容易的进一步处理?

I don't know if this was the thinking behind it, but that ease of processing (even though instantiate a list with the tuple data is not that hard) would come at possible confusing behavior.我不知道这是否是它背后的想法,但是处理的简便性(即使用tuple数据实例化一个list并不难)可能会出现令人困惑的行为。

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

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

fce1(1, 2, 3)

Could surprise people writing fce1 code not realizing that args they deal with later on are not what the function was called with.编写fce1代码的人可能会感到惊讶,因为他们没有意识到他们稍后处理的args不是调用函数时所用的。

I would also presume immutable types are easier to deal with internally and come with less overhead.我还认为不可变类型在内部更容易处理并且开销更少。

Why not?为什么不? The thing about tuple is, that you can not change it after creation.关于元组的事情是,你不能在创建后改变它。 This allows to increase speed of executing your script, and you do not really need a list for your function arguments, because you do not really need to modify the given arguments of a function.这允许提高执行脚本的速度,并且您实际上不需要函数参数列表,因为您实际上不需要修改函数的给定参数。 Would you need append or remove methods for your arguments?你需要为你的参数附加或删除方法吗? At most cases it would be no.在大多数情况下,它不会。 Do you want your program run faster.你想让你的程序运行得更快吗? That would be yes.那会是的。 And that's the way the most people would prefer to have things.这就是大多数人喜欢拥有东西的方式。 The *args thing returns tuple because of that, and if you really need a list, you can transform it with one line of code! *args 的东西因此返回元组,如果你真的需要一个列表,你可以用一行代码来转换它!

args = list(args)

So in general: It speeds up your program execution.所以总的来说:它可以加快您的程序执行速度。 You do not it to change the arguments.你不要改变论据。 It is not that hard to change it's type.改变它的类型并不难。

My best guess would be that if *args generates a list(mutable), it can lead to very surprising results for a multitude of situations.我最好的猜测是,如果 *args 生成一个列表(可变),它会在多种情况下导致非常令人惊讶的结果。 @Ondrej K. has given a great example. @Ondrej K. 给出了一个很好的例子。 As an analogy, when having a list as a default argument, every function call might have different default arguments.打个比方,当有一个列表作为默认参数时,每个函数调用可能有不同的默认参数。 This is the result of default arguments being evaluated only once, and this situation is not the most intuitive.这是默认参数只计算一次的结果,这种情况不是最直观的。 Even the official python docs have a specific workaround for this exact situation.即使是官方的 python 文档也有针对这种情况的特定解决方法。

Default parameter values are evaluated from left to right when the function definition is executed.执行函数定义时从左到右计算默认参数值。 This means that the expression is evaluated once, when the function is defined, and that the same “pre-computed” value is used for each call.这意味着表达式会在定义函数时计算一次,并且每次调用都使用相同的“预计算”值。 This is especially important to understand when a default parameter is a mutable object, such as a list or a dictionary: if the function modifies the object (eg by appending an item to a list), the default value is in effect modified.这对于理解何时默认参数是可变对象(例如列表或字典)尤其重要:如果函数修改了对象(例如,通过将项目附加到列表),则默认值实际上被修改了。 This is generally not what was intended.这通常不是预期的。 A way around this is to use None as the default, and explicitly test for it in the body of the function, eg:解决这个问题的一种方法是使用 None 作为默认值,并在函数体中明确测试它,例如:

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

Source documentation 源文件

To summarize, I believe that *args is a tuple because having it as a list would cause all the problems associated with a mutable type (like slower speed) and the bigger issue would be that most do not expect function arguments to change.总而言之,我相信 *args 是一个元组,因为将它作为列表会导致与可变类型相关的所有问题(如速度较慢),更大的问题是大多数人不希望函数参数发生变化。 Although I do agree that this implementation is very inconsistent with PEP-3132 and will cause confusion for most learners.尽管我确实同意这种实现与PEP-3132非常不一致,并且会导致大多数学习者感到困惑。 I am very new to Python and it took me a while to understand what might be the reason for *args to be a tuple and not a list for the sake of consistency with PEP-3132's acceptance.我对 Python 非常陌生,为了与PEP-3132 的接受保持一致,我花了一段时间才理解 *args 成为元组而不是列表的原因可能是什么。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM