2015-04-25 1 views
29

Это появилось в a recent PyCon talk.Почему допустимо назначать пустой список, но не пустой кортеж?

Заявление

[] = [] 

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

[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() 

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

+3

@ozgur, но распаковка с помощью кортежей действительно работает: 'a, b = 1, 2' is valid ... –

+0

Я не уверен, но я думаю, что' [] = [] 'не распаковывается. Однако я получил удивление, когда увидел, что это выполнимо: '[a, b] = [1, 2]'. Вместо этого я бы сделал 'a, b = (1, 2)' – ozgur

+4

У меня такое ощущение, что здесь не будет никакого интересного принципа. Лучший ответ, вероятно, будет похож на «вот раздел генератора кода, где он проверяет правильность LHS назначения, и вот проверка, которая ловит'() ', но позволяет' [] 'through". Возможно, это будет потому, что '()' распознается как константа или что-то в этом роде. – user2357112

ответ

19

Комментарий пользователя @ 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 s имеют явную проверку, что длина не равна нулю и вызовет ошибку, если она есть. list s не имеют такой проверки, поэтому никаких исключений не возникает.

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

+0

Совершенно легально в python 2.7 иметь кортеж длины 0. – clj

+6

@clj: Не на левой стороне задания это не так. – user2357112

+1

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

12

я решил попробовать использовать 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. Эта разница варьируется от the specification, которая заявляет:

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

...

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

Так это выглядит, как вы нашли законный, хотя в конечном счете, несущественным, ошибка в CPython (2.7.8 и 3.4.1 тестировалось).

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

+2

[Согласно спецификации] (https://docs.python.org/3/reference/simple_stmts.html#assignment-statements), он должен отклонять как '[] = []' и '() = []' , – jfs

+0

Я принял ответ @ Blckknght, потому что он точно отвечает на мой вопрос, но этот тоже замечательный! Раньше не знал о '' dis''. – j0ker

4

Это ошибка.

http://bugs.python.org/issue23275

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

2

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

Во всех случаях вы распаковкой: интерпретатор Python создает распаковку команду из всех трех способов, чтобы написать это, нет никаких списков или кортежей, связанных с левой стороны (код любезно /u/old-man-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') 

Analoguously для более или менее имен на левой стороне.

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

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