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
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