Search code examples
pythonpython-itertools

Python's itertools permutation different behavior in list comprehension when storing it in a variable


Given a list l=[1,2] I am trying to create a product of permutations. In other words, I am trying to create the following.

from itertools import permutations
l=[1,2]
print([(e1, e2) for e1 in permutations(l) for e2 in permutations(l)])

The above code prints [((1, 2), (1, 2)), ((1, 2), (2, 1)), ((2, 1), (1, 2)), ((2, 1), (2, 1))] as expected.

However, if I use the code below,

from itertools import permutations
l=[1,2]
lp1 = permutations(l)
lp2 = permutations(l)
print([(e1, e2) for e1 in lp1 for e2 in lp2])

The code prints [((1, 2), (1, 2)), ((1, 2), (2, 1))] .

I guess this is due to lp1 and lp2 pointing to the same iterator. But I do not understand why this is the case. Is this a intended behavior or is this a bug?


Solution

  • Yes. permutation is a generator. You can use it only once.

    Just to illustrate with an easier example, it is exactly as if you tried

    for i in range(3):
        for j in enumerate([1,2,3]):
             print(i,j)
    

    To get

    0 (0, 1)
    0 (1, 2)
    0 (2, 3)
    1 (0, 1)
    1 (1, 2)
    1 (2, 3)
    2 (0, 1)
    2 (1, 2)
    2 (2, 3)
    

    And then were surprised that

    range1=range(3)
    range2=enumerate([1,2,3])
    for i in range1:
        for j in range2:
             print(i,j)
    

    was not working as expected, and gives:

    0 (0, 1)
    0 (1, 2)
    0 (2, 3)
    

    Because you need to recreate range2 for each i iteration. Otherwise, j with iterates only once. The 2 other times, the iterator is over.

    (Edit note: initialy I used range(3) in my example for range2. But that is not a good example, since a range is a range, not a generator. So you can use it several times)

    Another, simpler way to see it is

    r=itertools.permutations([1,2])
    list(r)
    list(r)
    

    First time, it gives the expected list. Containing everything generated by the generator `permutations([1,2]).

    Second time, it gives an empty list. Because generator has nothing else to generate.