If I'm not mistaken, an iterator is just a copy of a pointer to some data.
This is sort of correct, but not entirely. The main confusion is that you're not directly handling any iterators in your second code block. There is an iterator being used (inside the for loop), and there is a reference (a pointer in other words) to some data, but they're not the same thing. The bit variable is only getting a reference to the value yielded by the iterator. It's not the iterator itself.
You can write your own version of a for loop, explicitly handling the iterator. Instead of for bit in A:, you'd have:
_iterator = iter(A)
while True:
try:
bit = next(_iterator)
except StopIteration:
break
# body of the for loop goes here!
del _iterator
Notice that bit gets its value assigned on each cycle of the loop. If you reassign it later (in the body of the original loop), neither the original value or the list it came from gets modified.
You could see a similar thing happen in a slightly modified version of your first code:
def flipBits(A):
for i in range(len(A)):
bit = A[i]
if bit == 1:
bit = 0
else:
bit = 1
return A
This version will not modify A for the same reason the for loop version of your code did not. Rebinding bit (which is a local variable) is not the same thing as rebinding A[i], even though they are both references to the same value.
Note that if you don't need to modify A in place, a more "Pythonic" way to solve this problem is to create a new list with the flipped bits in it, using a list comprehension:
def flipBits(A):
return [1-bit for bit in A] # you could also use "0 if bit == 1 else 1" instead of 1-bit
b = ...you will replace thebreference. You will not update any other object referenced by the oldb.=in both cases. I have an intuitive feel for this but I struggle to create an explanation.A[0] = ...will replace the first item inAwith a new reference.