Reputation: 5988
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?
Upvotes: 9
Views: 954
Reputation: 1124238
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.
Upvotes: 7
Reputation: 388223
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.
Upvotes: 12