mcdito13
mcdito13

Reputation: 321

Python Iterators vs Indexing

As I was writing a function I came upon something interesting when I tried two methods. One method used indexing, and the other used iterators:

Indexing Method:

A = [1,1,1,1]

def flipBits(A):
  for i in range(len(A)):
      if A[i] == 1: 
          A[i] = 0
      else: 
          A[i] = 1
  return A

>>> flipBits(A)
[0, 0, 0, 0]

Iterator Method:

A = [1,1,1,1]

def flipBits(A):
  for bit in A:
      if bit == 1: 
          bit = 0
      else: 
          bit = 1
  return A

>>> flipBits(A)
[1, 1, 1, 1]

If I'm not mistaken, an iterator is just a copy of a pointer to some data. So when I try to set the bit to something, it's only setting the copy, not the actual element of the list.

Is this correct? Anything else I should know? Thanks.

Upvotes: 1

Views: 1064

Answers (2)

Blckknght
Blckknght

Reputation: 104752

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

Upvotes: 2

Mark Ransom
Mark Ransom

Reputation: 308382

When you use a for loop, you create a new reference variable that is set to each of the objects in the sequence in turn.

When you use =, it sets the reference on the left to the object on the right.

In the first case, A[i] is a direct reference to an element of your list so the list gets updated.

In the second case, b is a reference that is separate from the list; it was created by the for loop. Updating it does not change the original list.

Upvotes: 1

Related Questions