I am writing simple code to reverse a linked list and realised the assignments can be done on one line, which I found pretty cool:
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
But I noticed that the code fails if I reverse the order of assignments between curr_node.next on the LHS (corresponding with prev_node on the RHS) and curr_node on the LHS (...curr_node.next on the RHS)
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
The input is
1 2 3 4
The output is
1
2
None
But the following error is produced from the while loop (only on the second block of code; the first one runs fine)
prev_node, curr_node, curr_node.next = curr_node, curr_node.next, prev_node
AttributeError: 'NoneType' object has no attribute 'next'
The closest discussion I could find on the subject was here. Which says that the RHS is evaluated first, from left to right. Which I think means that curr_node is stored, then prev_node, then curr_node.next. Then they are assigned to prev_node, curr_node.next and curr_node, respectively. I don't see the difference between the first and second example. Am I missing something simple?
Does anyone know why the first example runs while the second one produces an error?
Yes, tuple assignments have the right-hand-side evaluated first (from left to right), and the assignments are executed, also from left to right.
From the Assignment statements documentation:
An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter yielding a tuple) and assigns the single resulting object to each of the target lists, from left to right.
In your second case, you assigned None
to curr_node
, so the next assignment to curr_node.next
fails.
In other words, this works:
prev_node, curr_node.next, curr_node = curr_node, None, None
(provided curr_node
is still a Node instance with a next
attribute when curr_node.next = None
is executed), but if you swap the arguments:
prev_node, curr_node, curr_node.next = curr_node, None, None
and now curr_node = None
is executed before curr_node.next = prev_node
.
Here is a simplified demo to show you the order of the assignment matters:
>>> 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'