3

Looking at this answer, it seems that using a list comprehension (or for loop with append) is equivalent to calling list(..) on an iterator. Since generators are iterators too, I'd expect the same for generators. However, if you run

def permute(xs, count, low = 0):
    if low + 1 >= count:
        yield xs
    else:
        for p in permute(xs, low + 1):
            yield p
        for i in range(low + 1, count):
            xs[low], xs[i] = xs[i], xs[low]
            for p in permute(xs, low + 1):
                yield p
            xs[low], xs[i] = xs[i], xs[low]

print("Direct iteration")
for x in permute([1, 2], 2):
    print(x)
print("Listing")
for x in list(permute([1, 2], 2)):
  print(x)

It prints:

Direct iteration
[1, 2]
[2, 1]
Listing
[1, 2]
[1, 2]

Why is this happening?

typesanitizer
  • 2,505
  • 1
  • 20
  • 44

1 Answers1

3

You're modifying and yielding the same list xs over and over. When the generator is running the list contents are changing. It looks like it's working because although each print(x) prints the same list object, that object has different contents each time.

On the other hand, the second loop runs the generator to completion and collects all of the list references up. Then it prints out the lists—except they're all the same list, so every line is the same!

Change the two print(x) lines to print(x, id(x)) and you'll see what I mean. The ID numbers will all be identical.

Direct iteration
[1, 2] 140685039497928
[2, 1] 140685039497928
Listing
[1, 2] 140685039497736
[1, 2] 140685039497736

A quick fix is to yield copies of the list instead of the original list. The yield p's are fine, but yield xs should become:

yield xs[:]

With that fix, the results are as expected:

Direct iteration
[1, 2] 140449546108424
[2, 1] 140449546108744
Listing
[1, 2] 140449546108424
[2, 1] 140449546108808

Same results from both loops, and the ID numbers are different.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578