Распаковка аргументов: только выраженные аргументы могут следовать за выражением

В Python красиво работает:

def f(x,y,z): return [x,y,z]

a=[1,2]

f(3,*a)

Элементы a распаковываются, как если бы вы назвали его как f(3,1,2), и он возвращает [3,1,2]. Замечательно!

Но я не могу распаковать элементы a в первые два аргумента:

f(*a,3)

Вместо того, чтобы называть это как f(1,2,3), я получаю "SyntaxError: только аргументы named могут следовать за выражением".

Мне просто интересно, почему это должно быть так, и если есть какой-нибудь умный трюк, который я, возможно, не знал о распаковке массивов в произвольные части списков аргументов, не прибегая к временным переменным.

Ответ 1

Как указывает ответ Раймонда Хеттингера, это может измениться в Python 3 и вот связанное предложение, которое было принято. Особенно, связанный с текущим вопросом, здесь было одно из возможных изменений в этом предложении, которое обсуждалось:

Разрешить выделенное выражение как последний элемент в exprlist. Это упростило бы распаковывая код немного и позволяя выделенному выражению присваивать итератору. Эта поведение было отклонено, потому что это было бы слишком неожиданным.

Итак, есть причины для ограничения с аргументами распаковки функций, но это действительно немного удивительно!

Тем временем, обходной путь, который я искал, очевиден в ретроспективе:

f(*(a+[3]))

Ответ 2

Это не должно быть так. Просто Гвидо считал разумным.

В Python 3 правила для распаковки были несколько либерализованы:

>>> a, *b, c = range(10)
>>> a
0
>>> b
[1, 2, 3, 4, 5, 6, 7, 8]
>>> c
9

В зависимости от того, сможет ли Гвидо улучшить язык, эта либерализация также может быть расширена до функциональных аргументов.

См. обсуждение расширенной итеративной распаковки для некоторых мыслей о том, почему Python 3 изменил правила.

Ответ 3

Благодаря PEP 448 - Дополнительные распаковки обобщений,

f(*a, 3)

является теперь принятым синтаксисом, начиная с Python 3.5. Аналогично, вы можете использовать двунаправленную ** для распараллеливания аргументов аргументов в аргументах где угодно, и один из них можно использовать несколько раз.

Ответ 4

f ожидает 3 аргумента (x, y, z, в этом порядке).

Предположим L = [1,2]. Когда вы вызываете f(3, *L), что питон делает за кулисами, нужно вызвать f(3, 1, 2), не зная длины L.

Итак, что произойдет, если вместо L было [1,2,3]?

Затем, когда вы вызываете f(3, *L), вы в конечном итоге вызываете f(3,1,2,3), что будет ошибкой, потому что f ожидает ровно 3 аргумента, и вы дали ему 4.

Теперь предположим, что L=[1,2]1. Look at what happens when you call f`:

>>> f(3,*L) # works fine
>>> f(*L) # will give you an error when f(1,2) is called; insufficient arguments

Теперь вы неявно знаете, когда вы вызываете f(*L, 3), что 3 будет назначено на z, но python этого не знает. Он знает только, что последние j многие элементы ввода для f будут определяться содержимым L. Но поскольку он не знает значения len(L), он не может делать предположений о том, имеет ли f(*L,3) правильное количество аргументов.

Это, однако, не относится к f(3,*L). В этом случае python знает, что все аргументы EXCEPT первого будут определены содержимым L.

Но если вы назвали аргументы f(x=1, y=2, z=3), то аргументы, назначенные по имени, будут связаны сначала. Только тогда связаны позиционные аргументы. Итак, вы делаете f(*L, z=3). В этом случае z сначала привязывается к 3, а затем остальные значения привязываются.

Теперь интересно, если вы сделали f(*L, y=3), это даст вам ошибку для попыток назначить y дважды (один раз с ключевым словом, еще раз с позицией)

Надеюсь, что это поможет

Ответ 5

Ницца. Это также работает для кортежей. Не забывайте запятую:

a = (1,2)
f(*(a+(3,)))