Порядок операций для трех (или более) различных назначений переменных с использованием связанного списка в python 3

Я пишу простой код для реверсирования связанного списка и понял, что присваивания можно выполнять в одной строке, что мне показалось довольно крутым:

    def Reverse(head):
      prev_node = None
      curr_node = head

      while curr_node:
        prev_node, curr_node.next, curr_node = curr_node, prev_node, curr_node.next

      return prev_node

Но я заметил, что код дает сбой, если я изменяю порядок назначений между curr_node.next в левой части (соответствует prev_node в правой части) и curr_node в левой части (...curr_node.next в правой части)

    def Reverse(head):
      prev_node = None
      curr_node = head
      print(curr_node.data)
      print(curr_node.next.data)
      print(prev_node)

      while curr_node:
        prev_node, curr_node, curr_node.next = curr_node, curr_node.next, prev_node

      return prev_node

Вход

1 2 3 4

Выход

1
2
None

Но из цикла while возникает следующая ошибка (только во втором блоке кода, первый работает нормально)

      prev_node, curr_node, curr_node.next = curr_node, curr_node.next, prev_node
    AttributeError: 'NoneType' object has no attribute 'next'

Самое близкое обсуждение, которое я смог найти по этому вопросу, было здесь. Что говорит о том, что RHS сначала оценивается слева направо. Я думаю, это означает, что хранится curr_node, затем prev_node, затем curr_node.next. Затем они назначаются prev_node, curr_node.next и curr_node соответственно. Я не вижу разницы между первым и вторым примером. Я пропустил что-то простое?

Кто-нибудь знает, почему первый пример запускается, а второй выдает ошибку?


person Evan Parrish    schedule 16.06.2017    source источник


Ответы (1)


Да, при присвоении кортежа сначала оценивается правая сторона (слева направо), и присваивание выполняется также слева направо.

Из документации по операторам присваивания. :

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

Во втором случае вы присвоили None curr_node, поэтому следующее назначение curr_node.next не выполняется.

Другими словами, это работает:

prev_node, curr_node.next, curr_node = curr_node, None, None

(при условии, что curr_node по-прежнему является экземпляром Node с атрибутом next, когда выполняется curr_node.next = None), но если вы поменяете аргументы:

prev_node, curr_node, curr_node.next = curr_node, None, None

и теперь curr_node = None выполняется раньше curr_node.next = prev_node.

Вот упрощенная демонстрация, показывающая порядок выполнения заданий:

>>> class Node:
...     next = None
...
>>> curr_node = Node()
>>> curr_node.next, curr_node = None, None  # first attribute, then name
>>> curr_node = Node()
>>> curr_node, curr_node.next = None, None  # first the name, no attribute anymore
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'next'
person Martijn Pieters    schedule 16.06.2017
comment
Обычно вы хотите, чтобы одна операция выполнялась раньше другой, но при однострочном назначении кортеж создается из существующих значений в памяти, а затем выполняются назначения. Пожалуйста, поправьте меня, если я ошибаюсь. - person cs95; 16.06.2017
comment
@Coldspeed: почти так и происходит. Для кортежей с 2 ​​или 3 значениями значения помещаются в стек напрямую, без предварительного создания кортежа. См. Как внутри работает замена элементов в кортежах Python (a,b)=(b,a)? - person Martijn Pieters; 16.06.2017
comment
Вау, ребята, рискуя поставить себя в неловкое положение, я просто скажу, что это был мой первый раз, когда я задавал вопрос на этом сайте - наконец-то создал учетную запись - и я не разочарован!! @MartijnPieters Очень благодарен за подробности в вашем ответе, а также за последующее обсуждение. Я сделаю все возможное, чтобы заплатить вперед и ответить на вопросы, на которые (я думаю, что) я имею право ответить. - person Evan Parrish; 18.06.2017