Почему допустимо назначать пустой список, но не пустой кортеж?

Это появилось в недавнем обсуждении PyCon.

Утверждение

[] = []

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

[a, b] = [1, 2]

делает то, что вы ожидаете. Как логическое следствие, это также должно работать, когда количество распакованных элементов равно 0, что объясняет, почему присвоение пустого списка является допустимым. Эта теория далее поддерживается тем, что происходит, когда вы пытаетесь назначить непустой список в пустой список:

>>> [] = [1]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack

Я был бы доволен этим объяснением, если бы то же самое было бы верно для кортежей. Если мы можем распаковать список с 0 элементами, мы также должны иметь возможность распаковывать кортеж с 0 элементами, нет? Однако:

>>> () = ()
  File "<stdin>", line 1
SyntaxError: can't assign to ()

Похоже, что правила распаковки не применяются для кортежей, как для списков. Я не могу придумать никаких объяснений этой непоследовательности. Есть ли причина такого поведения?

Ответ 1

Комментарий пользователя @user2357112 о том, что это кажется совпадением, кажется правильным. Соответствующая часть исходного кода Python находится в Python/ast.c:

switch (e->kind) {
    # several cases snipped
    case List_kind:
        e->v.List.ctx = ctx;
        s = e->v.List.elts;
        break;
    case Tuple_kind:
        if (asdl_seq_LEN(e->v.Tuple.elts))  {
            e->v.Tuple.ctx = ctx;
            s = e->v.Tuple.elts;
        }
        else {
            expr_name = "()";
        }
        break;
    # several more cases snipped
}
/* Check for error string set by switch */
if (expr_name) {
    char buf[300];
    PyOS_snprintf(buf, sizeof(buf),
                  "can't %s %s",
                  ctx == Store ? "assign to" : "delete",
                  expr_name);
    return ast_error(c, n, buf);
}

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

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

Ответ 2

Я решил попробовать использовать dis, чтобы понять, что происходит здесь, когда я споткнулся о чем-то любопытном:

>>> def foo():
...   [] = []
... 
>>> dis.dis(foo)
  2           0 BUILD_LIST               0
              3 UNPACK_SEQUENCE          0
              6 LOAD_CONST               0 (None)
              9 RETURN_VALUE        
>>> def bar():
...   () = ()
... 
  File "<stdin>", line 2
SyntaxError: can't assign to ()

Каким-то образом компилятор Python имеет специальные случаи для пустого кортежа на LHS. Эта разница варьируется от спецификации, которая гласит:

Назначение объекта одной цели рекурсивно определяется следующим образом.

...

  • Если цель является целевым списком, заключенным в круглые скобки или в квадратные скобки: объект должен быть итерабельным с тем же количеством элементов, что и целевые объекты в целевом списке, а его элементы назначаются слева направо, к соответствующим целям.

Итак, похоже, что вы нашли законную, хотя в конечном счете, несущественную ошибку в CPython (2.7.8 и 3.4.1).

IronPython 2.6.1 демонстрирует ту же самую разницу, но Jython 2.7b3 + имеет странное поведение, при этом () = () начинает утверждение, похоже, никак не может его закончить.

Ответ 3

Это ошибка.

http://bugs.python.org/issue23275

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

Ответ 4

"Присвоение списку" - неправильный способ подумать об этом.

Во всех случаях вы распаковываете: интерпретатор Python создает инструкцию для распаковки всех трех способов ее записи, в левой части нет списков или кортежей (код предоставлен /и/старый человек-Prismo):

>>> def f():
...     iterable = [1, 2]
...     a, b = iterable
...     (c, d) = iterable
...     [e, f] = iterable
...
>>> from dis import dis
>>> dis(f)
  2           0 LOAD_CONST               1 (1)
              3 LOAD_CONST               2 (2)
              6 BUILD_LIST               2
              9 STORE_FAST               0 (iterable)

  3          12 LOAD_FAST                0 (iterable)
             15 UNPACK_SEQUENCE          2
             18 STORE_FAST               1 (a)
             21 STORE_FAST               2 (b)

  4          24 LOAD_FAST                0 (iterable)
             27 UNPACK_SEQUENCE          2
             30 STORE_FAST               3 (c)
             33 STORE_FAST               4 (d)

  5          36 LOAD_FAST                0 (iterable)
             39 UNPACK_SEQUENCE          2
             42 STORE_FAST               5 (e)
             45 STORE_FAST               6 (f)
             48 LOAD_CONST               0 (None)
             51 RETURN_VALUE      

Как вы можете видеть, все три утверждения точно совпадают.

Что теперь распаковывается в основном:

_iterator = iter(some_iterable)
a = next(_iterator)
b = next(_iterator)
for superfluous_element in _iterator:
    # this only happens if there’s something left
    raise SyntaxError('Expected some_iterable to have 2 elements')

Аналогично для более или менее имен с левой стороны.

Теперь, когда @blckknght сказал: компилятор по какой-то причине проверяет, является ли левая сторона пустой кортеж и запрещает это, но не если ее пустой список.

Его единственная последовательность и логичность, позволяющая присваивать 0 именам: Почему бы и нет? Вы просто утверждаете, что итерабельность с правой стороны пуста. Это мнение также похоже на консенсус в отчете @gecko: @Позволяет () = iterable.