Weird behaviour of a generator function while converting it to a list
Question:
I am trying to get every state of a list while it is being sorted for a visualization.
So with bubbleSort algorithm
i made a generator function :
def bubbleSort(arr):
n = len(arr)
yield arr # yielding original state
for i in range(n):
swapped = False
for j in range(0, n-i-1):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
swapped = True
yield arr # yielding every state during bubbleSort
if (swapped == False):
break
arr = [64, 34, 25, 12, 22, 11, 90]
generator=bubbleSort(arr)
for i in generator:
print(i)
Now this gives me my desired output.
[64, 34, 25, 12, 22, 11, 90]
[34, 64, 25, 12, 22, 11, 90]
[34, 25, 64, 12, 22, 11, 90]
[34, 25, 12, 64, 22, 11, 90]
[34, 25, 12, 22, 64, 11, 90]
[34, 25, 12, 22, 11, 64, 90]
[25, 34, 12, 22, 11, 64, 90]
[25, 12, 34, 22, 11, 64, 90]
[25, 12, 22, 34, 11, 64, 90]
[25, 12, 22, 11, 34, 64, 90]
[12, 25, 22, 11, 34, 64, 90]
[12, 22, 25, 11, 34, 64, 90]
[12, 22, 11, 25, 34, 64, 90]
[12, 11, 22, 25, 34, 64, 90]
[11, 12, 22, 25, 34, 64, 90]
But if i convert it to a list using any of these ways: (One at a time)
#1
print(list(generator))
#2
l=[i for i in generator]
print(l)
#3
l=[]
for i in generator:
l.append(i)
print(l)
#4
l=[*generator]
print(l)
It will give me o/p as below : (all states are already sorted or last states)
[[11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90]]
Now i don’t understand why this happens but , this gets even more confusing for me when i do
any of these and it gives me the desired o/p.
#1
l=[list(i) for i in generator] #list(i)
print(l)
#2
l=[i.copy() for i in generator] #i.copy()
print(l)
#3
l=[]
for i in generator:
l.append(list(i)) #list(i)
print(l)
#4
l=[]
for i in generator:
l.append(i.copy()) #i.copy()
print(l)
o/p:
[[64, 34, 25, 12, 22, 11, 90], [34, 64, 25, 12, 22, 11, 90], [34, 25, 64, 12, 22, 11, 90], [34, 25, 12, 64, 22, 11, 90], [34, 25, 12, 22, 64, 11, 90], [34, 25, 12, 22, 11, 64, 90], [25, 34, 12, 22, 11, 64, 90], [25, 12, 34, 22, 11, 64, 90], [25, 12, 22, 34, 11, 64, 90], [25, 12, 22, 11, 34, 64, 90], [12, 25, 22, 11, 34, 64, 90], [12, 22, 25, 11, 34, 64, 90], [12, 22, 11, 25, 34, 64, 90], [12, 11, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90]]
Can anyone explain it to me why the difference in outputs between last 2 inputs ?
What changes when i do i.copy() or list(i) for every state in the generator ?
Thanks for reading.
Answers:
There is a single arr
object that is being mutated inside the generator.
If you print it in between the mutations, you get a snapshot of each individual state, since the current state of arr
is printed to the console before it changes.
When you capture that arr
object in a list comprehension, though, you just have a list with a bunch of references to that one object, so as arr
changes, so too does every entry in your list.
If you wanted to change that, you could yield arr.copy()
inside your generator to make sure that you’re always yielding a unique copy that won’t change as the generator continues to iterate. This would have the same effect that you’re seeing when you call copy()
or list()
outside of the generator (either of those methods results in a new copy which does not change when arr
is modified inside the generator).
I am trying to get every state of a list while it is being sorted for a visualization.
So with bubbleSort algorithm
i made a generator function :
def bubbleSort(arr):
n = len(arr)
yield arr # yielding original state
for i in range(n):
swapped = False
for j in range(0, n-i-1):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
swapped = True
yield arr # yielding every state during bubbleSort
if (swapped == False):
break
arr = [64, 34, 25, 12, 22, 11, 90]
generator=bubbleSort(arr)
for i in generator:
print(i)
Now this gives me my desired output.
[64, 34, 25, 12, 22, 11, 90]
[34, 64, 25, 12, 22, 11, 90]
[34, 25, 64, 12, 22, 11, 90]
[34, 25, 12, 64, 22, 11, 90]
[34, 25, 12, 22, 64, 11, 90]
[34, 25, 12, 22, 11, 64, 90]
[25, 34, 12, 22, 11, 64, 90]
[25, 12, 34, 22, 11, 64, 90]
[25, 12, 22, 34, 11, 64, 90]
[25, 12, 22, 11, 34, 64, 90]
[12, 25, 22, 11, 34, 64, 90]
[12, 22, 25, 11, 34, 64, 90]
[12, 22, 11, 25, 34, 64, 90]
[12, 11, 22, 25, 34, 64, 90]
[11, 12, 22, 25, 34, 64, 90]
But if i convert it to a list using any of these ways: (One at a time)
#1
print(list(generator))
#2
l=[i for i in generator]
print(l)
#3
l=[]
for i in generator:
l.append(i)
print(l)
#4
l=[*generator]
print(l)
It will give me o/p as below : (all states are already sorted or last states)
[[11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90]]
Now i don’t understand why this happens but , this gets even more confusing for me when i do
any of these and it gives me the desired o/p.
#1
l=[list(i) for i in generator] #list(i)
print(l)
#2
l=[i.copy() for i in generator] #i.copy()
print(l)
#3
l=[]
for i in generator:
l.append(list(i)) #list(i)
print(l)
#4
l=[]
for i in generator:
l.append(i.copy()) #i.copy()
print(l)
o/p:
[[64, 34, 25, 12, 22, 11, 90], [34, 64, 25, 12, 22, 11, 90], [34, 25, 64, 12, 22, 11, 90], [34, 25, 12, 64, 22, 11, 90], [34, 25, 12, 22, 64, 11, 90], [34, 25, 12, 22, 11, 64, 90], [25, 34, 12, 22, 11, 64, 90], [25, 12, 34, 22, 11, 64, 90], [25, 12, 22, 34, 11, 64, 90], [25, 12, 22, 11, 34, 64, 90], [12, 25, 22, 11, 34, 64, 90], [12, 22, 25, 11, 34, 64, 90], [12, 22, 11, 25, 34, 64, 90], [12, 11, 22, 25, 34, 64, 90], [11, 12, 22, 25, 34, 64, 90]]
Can anyone explain it to me why the difference in outputs between last 2 inputs ?
What changes when i do i.copy() or list(i) for every state in the generator ?
Thanks for reading.
There is a single arr
object that is being mutated inside the generator.
If you print it in between the mutations, you get a snapshot of each individual state, since the current state of arr
is printed to the console before it changes.
When you capture that arr
object in a list comprehension, though, you just have a list with a bunch of references to that one object, so as arr
changes, so too does every entry in your list.
If you wanted to change that, you could yield arr.copy()
inside your generator to make sure that you’re always yielding a unique copy that won’t change as the generator continues to iterate. This would have the same effect that you’re seeing when you call copy()
or list()
outside of the generator (either of those methods results in a new copy which does not change when arr
is modified inside the generator).