Что такое модель исполнения для * args в вызове функции?
Мне нужно передать огромную list
/tuple
функцию через *args
.
def f(*args): # defined in foreign module
pass
arguments = tuple(range(10000))
f(*arguments)
И мне интересно, что происходит при вызове функции.
Поддерживает ли он arguments
аналогичную любую позиционную переменную: сохранить ее и получить доступ по требованию во время выполнения тела? Или он выполняет итерацию через arguments
еще до выполнения тела, расширяя позиционные аргументы? Или это что-то еще?
Ответы
Ответ 1
Да, синтаксис вызова *arguments
должен повторять итерацию arguments
по двум причинам:
-
Вы передаете список, но аргумент *args
переменной размера в функции является кортежем. Таким образом, элементы должны быть скопированы здесь.
-
Синтаксис вызова должен использоваться для любой функции, где у вас могут быть фактические позиционные аргументы вместо или в дополнение к переменной *varargs
.
Например, если сигнатура функции def f(foo, *args):
, то первый элемент должен быть передан отдельно.
В принципе, CPython может оптимизировать для случая, когда все значения кортежа, используемые в вызове с function(*tupleargs)
, заканчиваются аргументом *varargs
и повторно используют этот кортеж. Однако на самом деле это не все, что распространено, и никто не сделал этого.
Обратите внимание, что для синтаксиса вызова **kwargs
добавленная проблема изменчивости делает обмен используемым объектом действительно плохую идею; вам нужно создать копию используемого dict, потому что иначе функция или вызывающий может изменить этот словарь с изменениями, отраженными в другой ссылке.
Ответ 2
Простой тест с использованием генератора:
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
Как вы можете видеть из вывода, передача *arguments
фактически распакует всю итерабельность, так как технически вы сообщаете Python передавать итерабельность как отдельные аргументы с использованием синтаксиса *arguments
. Не имеет значения, что в определении функции также используется *args
, что заставляет Python снова добавлять аргументы в кортеж.
Итак, вы распаковываете список, чтобы снова упаковать его здесь. Вы можете избежать этого, просто передав список напрямую.