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?
As what datatype are the *args stored, when passed into a function? List.
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.
__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.
**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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With