Семантика распаковки кортежа в python
Почему python разрешает только именованным аргументам следовать за распаковкой выражения в вызове функции?
>>> def f(a,b,c):
... print a, b, c
...
>>> f(*(1,2),3)
File "<stdin>", line 1
SyntaxError: only named arguments may follow *expression
Это просто эстетический выбор, или есть случаи, когда это допускает некоторые двусмысленности?
Ответы
Ответ 1
Я уверен, что причина, по которой людям "естественно" это не нравится, заключается в том, что она делает значение более поздних аргументов неоднозначным, в зависимости от длины интерполированного ряда:
def dangerbaby(a, b, *c):
hug(a)
kill(b)
>>> dangerbaby('puppy', 'bug')
killed bug
>>> cuddles = ['puppy']
>>> dangerbaby(*cuddles, 'bug')
killed bug
>>> cuddles.append('kitten')
>>> dangerbaby(*cuddles, 'bug')
killed kitten
вы не можете сказать, просто глядя на последние два звонка на dangerbaby
, который работает так, как ожидалось, и который убивает маленьких котят-пуффинов.
конечно, некоторые из этой неопределенности также присутствуют при интерполяции в конце. но путаница ограничивается интерполированной последовательностью - она не влияет на другие аргументы, такие как bug
.
[Я сделал быстрый поиск, чтобы увидеть, могу ли я найти что-нибудь официальное. кажется, что префикс * для varags был представленный в python 0.9.8. предыдущий синтаксис обсуждался здесь, и правила его работы были довольно сложными. так как добавление дополнительных аргументов "должно было" произойти в конце, когда не было маркера *, похоже, что это просто переносится. наконец, там упоминает здесь длинного обсуждения списков аргументов, которые не были отправлены по электронной почте.]
Ответ 2
Я подозреваю, что он согласован со звездообразной нотацией в определениях функций, которая в конце концов является моделью для обозначения звезды в вызовах функций.
В следующем определении параметр *c
будет обрабатывать все последующие аргументы без ключевого слова, поэтому, очевидно, когда вызывается f
, единственный способ передать значение для d
будет в качестве аргумента ключевого слова.
def f(a, b, *c, d=1):
print "slurped", len(c)
(Такие "параметры только для ключевого слова" поддерживаются только в Python 3. В Python 2 невозможно назначить значения после аргумента, помеченного звездочкой, поэтому вышеописанное является незаконным.)
Итак, в определении функции звездный аргумент должен следовать всем обычным позиционным аргументам. Вы заметили, что одно и то же правило было расширено для вызовов функций. Таким образом, синтаксис звезды согласован для деклараций функций и вызовов функций.
Другой parallelism заключается в том, что в вызове функции может быть только один (одиночный) аргумент. Следующее является незаконным, хотя можно легко представить, что это разрешено.
f(*(1,2), *(3,4))
Ответ 3
Прежде всего, очень просто предоставить очень похожий интерфейс, используя функцию обертки:
def applylast(func, arglist, *literalargs):
return func(*(literalargs + arglist))
applylast(f, (1, 2), 3) # equivalent to f(3, 1, 2)
Во-вторых, усиление интерпретатора для поддержки вашего синтаксиса изначально может привести к накладным расходам для очень критически важной активности приложения-функции. Даже если для скомпилированного кода требуется только несколько дополнительных инструкций, это может привести к неприемлемому снижению производительности в обмен на функцию, которая не предназначена для всего, что часто и легко размещается в пользовательской библиотеке.
Ответ 4
Некоторые наблюдения:
- Python обрабатывает позиционные аргументы перед аргументами ключевого слова (
f(c=3, *(1, 2))
в вашем примере все еще печатает 1 2 3
). Это имеет смысл, поскольку (i) большинство аргументов в вызовах функций являются позиционными, и (ii) семантика языка программирования должна быть однозначной (т.е. Выбор должен быть сделан либо в порядке, в котором обрабатывать позиционные и ключевые аргументы).
- Если бы в вызове функции у нас был позиционный аргумент справа, было бы трудно определить, что это значит. Если мы назовем
f(*(1, 2), 3)
, то должно быть f(1, 2, 3)
или f(3, 1, 2)
, и почему любой выбор имеет больше смысла, чем другой?
- Для официального объяснения PEP 3102 дает много информации о том, как работают определения функций. Звездочка (*) в определении функции указывает аргументы конца позиции (раздел Спецификация). Чтобы понять, почему, подумайте:
def g(a, b, *c, d)
. Нет способа предоставить значение для d
, кроме как в качестве аргумента ключевого слова (позиционные аргументы будут "схвачены" на c
).
- Важно понять, что это означает: поскольку звезда отмечает конец позиционных аргументов, это означает, что все позиционные аргументы должны находиться в этом положении или слева от него.
Ответ 5
измените порядок:
def f(c,a,b):
print(a,b,c)
f(3,*(1,2))
Ответ 6
Если у вас есть параметр Python 3 с ключевым словом, например
def f(*a, b=1):
...
то вы можете ожидать чего-то типа f(*(1, 2), 3)
для установки a
в (1 , 2)
и b
в 3
, но, конечно, даже если синтаксис, который вы хотите, был разрешен, это не так, потому что ключевое слово- только параметры должны быть только для ключевых слов, например f(*(1, 2), b=3)
. Если бы это было допустимо, я полагаю, что он должен был бы установить a
в (1, 2, 3)
и оставить b
в качестве значения по умолчанию 1
. Поэтому, возможно, это не синтаксическая двусмысленность, а двусмысленность в ожидаемом, что-то, что Python пытается избежать.