Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is execution model for *args in function call?

I need to pass huge list/tuple to function through *args.

def f(*args):  # defined in foreign module
    pass

arguments = tuple(range(10000))
f(*arguments)

And I wonder what happens at function call.

Does it handle arguments similar to any positional variable: save it and access on-demand during body execution? Or does it iterate through arguments even before body execution, extending positional arguments? Or is it something else?

like image 374
Mikhail M. Avatar asked Oct 25 '16 06:10

Mikhail M.


People also ask

What datatype are the * args stored when passed into a function?

As what datatype are the *args stored, when passed into a function? List.

Why is * args a tuple?

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.

What is KWDS in Python?

__init__(*args, **kwds) means: unpack the tuple args and the dictionary kwds into arguments so that they're passed separately to __init__ . If you don't use the * and ** then you're passing args and kwds as they are, which means you're getting a tuple and a dictionary.

What is a Kwarg?

**kwargs stands for keyword arguments. The only difference from args is that it uses keywords and returns the values in the form of a dictionary.


2 Answers

A simple test using a generator:

def gen():
    print('Yielding 1')
    yield 1
    print('Yielding 2')
    yield 2
    print('Yielding 3')
    yield 3


arguments = gen()
def f(*args):
    pass

f(*arguments)
# Yielding 1
# Yielding 2
# Yielding 3

As you can see from the output, passing *arguments will actually unpack the whole iterable, since technically, you are telling Python to pass the iterable as individual arguments using the *arguments syntax. It does not matter that the function definition also uses *args which makes Python pack the arguments back into a tuple again.

So yeah, you are unpacking the list only to pack it again here. You can avoid this by just passing the list directly.

like image 84
poke Avatar answered Oct 28 '22 04:10

poke


Yes, the *arguments call syntax has to iterate over the arguments iterable, for two reasons:

  • You are passing in a list, but the *args variable-size argument in the function is a tuple. So elements will have to be copied here.

  • The call syntax has to be usable for any function, where you may have actual positional arguments instead of, or in addition to, a *varargs variable.

    For example, if the function signature was def f(foo, *args):, then the first element would have to be passed in separately.

    In principle, CPython could optimise for the case where all values of a tuple used in a call with function(*tupleargs) end up in a *varargs argument, and re-use that tuple. However, this is actually not all that common and no-one has done this.

Note that for the **kwargs call syntax, the added challenge of mutability makes sharing the object used a really bad idea; you have to create a copy of the dict used because otherwise the function or the caller could mutate that dictionary with the changes reflected in the other reference.

like image 7
Martijn Pieters Avatar answered Oct 28 '22 03:10

Martijn Pieters