0

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?

1
  • 1
    The answers have made the problem clear, but in capsule form: The original version calls permutations() three times, once for e1 and twice for e2. That's why the iterator in the second version gets exhausted. Commented Jul 17, 2024 at 10:17

3 Answers 3

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the explanation! I forgot lp2 is exhausted after the first iteration, my bad.
1

This should be, because permutations() gives you a generator of permutations. Once you stepped through it, it is exhausted and won't generate any further elements. The permutations of 0,1 are 0,1 and 1,0, which both generators will provide you. Therefore, in the second version, you get exactly four elements.

In the first example, however, you create the permutation again and again for every iteration over the first permutation. Therefore, the code will step through the second permutations again, resulting in the longer list.

Comments

0

The difference between the two examples is:

[(e1, e2) for e1 in permutations(l) for e2 in permutations(l)]
#                                             ^^^^^^^^^^^^^^^

Here, a new permutations(l) is created for each e1.

lp2 = permutations(l)
[(e1, e2) for e1 in lp1 for e2 in lp2])

Here, the same lp2 is reused for each e1, but it is exhausted after the first time.


lp1 = permutations(l)
lp2 = permutations(l)

[…]

I guess this is due to lp1 and lp2 pointing to the same iterator.

No, lp1 and lp2 are distinct iterators.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.