Search code examples
pythonmemory-addresscycle

iterator.cycle function called twice return the same iterator (with same memory adress)


On python 3.7, I expect to get a new memory address (and cycle) each time I call iterator.cycle, however, this piece of code duplicates 2 iterators on 4:

from itertools import cycle

cycles = [[None] * 2] * 2
s = set()
for i in range(2):
    for j in range(2):
        c =  cycle("ab")
        cycles[i][j] =c
        s.add(hex(id(c)))
        print(hex(id(c)))

print(len(s))

It prints:

0x7efe576cc370
0x7efe576cc3c0
0x7efe576cc410
0x7efe576cc370
3

So object 0x7efe576cc370 is used twice. On the other hand, this code works:

from itertools import cycle

cycles = []

for _ in range(1000):
    c = cycle("ab")
    cycles.append(c)

s = set([hex(id(c)) for c in cycles])
print(len(s)) # 1000

Solution

  • The issue here is the way that you are constructing the cycles object.

    When you use the * operator on a list, the new list that is returned contains multiple references to the original list, not copies of the original list.

    As a result, cycles is a two item list, where both elements point to the same list, and thus changes made to cycles[1][0] also apply to cycles[0][0]

    When you assign to cycles[1][0], you also overwrite the item at cycles[0][0], which deletes the previous item.

    As to why this means s has a length of 3, this is due to the way the object allocator / garbage collector interact.

    For anything but the innermost list, you should use a list comprehension to construct the object.

     cycles = [[None] * 2 for _ in range(2)] 
    

    This will produce a list that is suitably constructed, and you code will work fine