[] =(),() =() и {} =() 'присвоения'

Я был удивлен, увидев в Python 3 следующее: первые два ничего не дают:

>>> [] = ()
>>> () = ()
>>> {} = ()
  File "<stdin>", line 1
SyntaxError: can't assign to literal

В Python 2.7 только первый ничего не вызывает:

>>> [] = ()
>>> () = ()
  File "<stdin>", line 1
SyntaxError: can't assign to ()
>>> {} = ()
  File "<stdin>", line 1
SyntaxError: can't assign to literal

Что здесь происходит? Почему ни один из них не поднимает ошибок? И почему был () =() предположительно добавлен, чтобы быть действительным в Python 3?

* Примечание. Вы можете заменить правую сторону любым пустым итерабельным (например, [] = set()), я просто выбираю пустой набор для иллюстрации

Ответ 1

Согласно Issue23275, это в основном причуды, не причиняющие никакого реального вреда, но также и никакой полезности. Обратите внимание, что [] =() не изменяет литерал list:

>>> [] = ()
>>> type([])
<class 'list'>

[] = x операторы [] = x основном утверждают, что x является итерабельным и что x пуст (хотя никто не рекомендовал бы их использовать таким образом), например

>>> [] = (1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
>>> [] = (1,)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack

Как замечает Джон Й, лучше всего думать о [] =() как не о назначении, а как о совместимости с синтаксисом распаковки Python.

Как отмечает ArrowCase, этот синтаксис также распространяется на несколько присвоений:

>>> a = [] = ()
>>> a
()

Глядя на байт-код CPython для множественного назначения, показано, что эти операции похожи на обычный итеративный синтаксис распаковки, используя инструкцию UNPACK_SEQUENCE:

>>> dis.dis('a = [] = ()')
  1           0 BUILD_TUPLE              0
              2 DUP_TOP
              4 STORE_NAME               0 (a)
              6 UNPACK_SEQUENCE          0
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE
>>> dis.dis('[a, b] = (1, 2)')
  1           0 LOAD_CONST               3 ((1, 2))
              2 UNPACK_SEQUENCE          2
              4 STORE_NAME               0 (a)
              6 STORE_NAME               1 (b)
              8 LOAD_CONST               2 (None)
             10 RETURN_VALUE

Тот же Issue23275 утверждает, что () =() был добавлен как допустимый синтаксис для Python 3 для согласования. Было решено, что удаление [] =() будет бесполезным сломать код, так как оно не наносит вреда и подходит с итерируемой логикой распаковки. {} =() по-прежнему недействителен, поскольку синтаксис распаковки не имеет смысла в этом контексте с фигурными скобками.

В случае, если кому-то интересно, синтаксис вроде list() =() просто синтаксически недействителен, потому что вы никогда не сможете назначить вызов функции.

Ответ 2

Существует способ назначения переменных из итерабельного:

>>> a, b = iter((1, 2))
>>> a
1
>>> b
2
>>> [c, d] = iter((4, 5))
>>> c
4
>>> d
5

Назначения [] = … и () = … кажутся особыми случаями.

Ответ 3

Левая сторона оператора присваивания не является выражением, это целевой список. Краткое содержание:

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

Это объясняет, почему [] и () являются действительными левыми сторонами для присвоений: они являются действительными целевыми списками. Однако {} не является, так как он не является допустимым целевым списком.

Конечно, {} может быть частью цели, например, как первичная для подписки: {}[()] = 0 является действительным python (но совершенно бесполезным, конечно).

Ответ 4

Это синтаксис для распаковки двухэлементных итераций в две цели назначения:

[x, y] = whatever

Это обобщает до трех или более целей, но также обобщает:

[x] = whatever

распаковывает одноэлементный итеративный объект в одну цель назначения и

[] = whatever

распаковывает нулевой элемент, итеративный в нулевые целевые назначения (что ничего не делает, если whatever равно итератор с нулевым элементом и генерирует исключение, если это не так).

() = whatever равно также распаковывает нулевой элемент итерабельным, но {} = whatever что нет; нет синтаксиса расставания для распаковки, который включает фигурные скобки.