Skip to content
Advertisement

Python comma assignments order [closed]

I was trying Python comma assignment to parallelly change the values of variables. I believe Python will evaluate the values of the expressions on the right-hand side first and then, assign those values to the variables on the left-hand side. One specific example is in this reverse linked list code:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        prev = None
        curr = head
        while curr != None:
            prev, curr.next, curr = curr, prev, curr.next
        return prev

prev, curr.next, curr = curr, prev, curr.next works perfectly fine. If I change the order to prev, curr, curr.next = curr, curr.next, prev, I expect my code works the same:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        prev = None
        curr = head
        while curr != None:
            prev, curr, curr.next = curr, curr.next, prev
        return prev

However, I got an error instead in the second code for that line:

AttributeError: 'NoneType' object has no attribute 'next'
    prev, curr, curr.next = curr, curr.next, prev

which doesn’t make sense to me because Python evaluates the right-hand side expressions first before assigning, which means the order of where I put the expressions shouldn’t matter. Am I missing something here?

Advertisement

Answer

You are correct that Python evaluates the right-side first; however, what’s missing is that Python will evaluate each side in order. We can see what this means by breaking the two options down into individual assignment statements (Full example on Hastebin):

# First example:
def reverseList(head: ListNode) -> ListNode:
    prev = None
    curr = head
    while curr != None:
        # prev, curr.next, curr = curr, prev, curr.next
        x = curr
        y = prev
        z = curr.next
        prev = x
        curr.next = y
        curr = z
    return prev
# Second example:
def reverseList2(head: ListNode) -> ListNode:
    prev = None
    curr = head
    while curr != None:
        # prev, curr, curr.next = curr, curr.next, prev
        x = curr
        y = curr.next
        z = prev
        prev = x
        curr = y
        curr.next = z
    return prev

The important part here is that in the first example, we assign the value of curr.next before we assign the value of curr, whereas in the second example we assign the value of curr.next after. What this means is that when we go to assign curr.next, we’ve already updated curr, so when the interpreter accesses curr.next again for another assignment it will pull the wrong reference back. The error occurs when you’ve set curr to None, and then after doing so, you then try to access curr.next to complete your reassignment operation.

A side note, try to refrain from using next as a variable name. As it’s a builtin function, it can cause some confusion, though it’s probably fine.

User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement