Подтвердить что ты не робот

Семантика распаковки кортежа в 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

Это просто эстетический выбор, или есть случаи, когда это допускает некоторые двусмысленности?

4b9b3361

Ответ 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 пытается избежать.