Here is a good example of what looks like a difference in scope, when using a variable referring to an int, vs a list.
Note the 'prev' variable:
1 class Solution:
2 def convertBST(self, root: TreeNode) -> TreeNode:
3
4 if root == None:
5 return root
6
7 prev = 0
8 def traverse(node):
9 if node.right:
10 traverse(node.right)
11 node.val += prev
12 prev = node.val
13 if node.left:
14 traverse(node.left)
15
16 traverse(root)
17 return root
This errors with the following message:
UnboundLocalError: local variable 'prev' referenced before assignment
node.val += prev
There is no error, if instead I replaced these lines with this code:
line 7: prev = [0]
line 11: node.val += prev[0]
line 12: prev[0]= node.val
making one believe that prev = 0 was invisible within the traverse function, while prev = [0] is visible!
But in fact, prev = 0 is also visible and can be used within the traverse function, if no assignment to it takes place i.e. UnboundLocalError occurs on line 11, only if line 12 is present.
Because of the immutability of an int, line 12 causes the variable prev within the traverse function to point to a new memory location, and this then "hides" the variable prev = 0 defined outside, causing the error.
But when a new int is assigned to prev[0], an element in a mutable list, the pointer for the first element of the list can be updated to point to the new int (both inside and outside the traverse function); no separate scope (ie local variable) is created, the original prev variable remains visible within the traverse function, and prev[0] can be used on line 11 before the assignment on line 12.
+=which for immutable data-structures returns and reassigns a new object to the local variable. Outside this local scope, these effects are no longer visible. In the second example, you use.append, which mutates the list in place, and the effect will be visible in any scope where the list is accessible (i.e. your global scope).