Перестановка itertools Python различное поведение при понимании списка при его сохранении в переменной

Учитывая список l=[1,2] Я пытаюсь создать продукт перестановок. Другими словами, я пытаюсь создать следующее.

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

Приведенный выше код печатает [((1, 2), (1, 2)), ((1, 2), (2, 1)), ((2, 1), (1, 2)), ((2, 1), (2, 1))], как и ожидалось.

Однако, если я использую приведенный ниже код,

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

Код печатается [((1, 2), (1, 2)), ((1, 2), (2, 1))] .

Я предполагаю, что это связано с тем, что lp1 и lp2 указывают на один и тот же итератор. Но я не понимаю, почему это так. Это задуманное поведение или это ошибка?

Ответы прояснили проблему, но в виде капсулы: в исходной версии permutations() вызывается три раза: один раз для e1 и дважды для e2. Поэтому итератор во второй версии исчерпывается.

alexis 17.07.2024 12:17
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
72
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Да. permutation — генератор. Вы можете использовать его только один раз.

Чтобы проиллюстрировать это на более простом примере, это точно так же, как если бы вы пытался

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

Получить

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)

А потом были удивлены, что

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

не работал должным образом и выдает:

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

Потому что вам нужно воссоздавать range2 для каждой i итерации. В противном случае j with выполняет итерацию только один раз. В двух других случаях итератор закончился.

(Примечание редактирования: изначально я использовал range(3) в своем примере для range2. Но это не очень хороший пример, поскольку диапазон — это диапазон, а не генератор. Поэтому вы можете использовать его несколько раз)

Другой, более простой способ увидеть это:

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

В первый раз он дает ожидаемый список. Содержит все, что генерирует генератор `permutations([1,2]).

Во второй раз выдает пустой список. Потому что генератору больше нечего генерировать.

Спасибо за объяснение! Я забыл, что lp2 исчерпан после первой итерации, моя вина.

utyde 17.07.2024 11:59

Так и должно быть, потому что permutations() дает вам генератор перестановок. Как только вы пройдете через него, он исчерпается и больше не будет генерировать никаких элементов. Перестановки 0,1 — это 0,1 и 1,0, которые вам предоставят оба генератора. Следовательно, во втором варианте вы получите ровно четыре элемента.

Однако в первом примере вы создаете перестановку снова и снова для каждой итерации первой перестановки. Таким образом, код снова пройдет через вторые перестановки, в результате чего список станет более длинным.

Разница между двумя примерами заключается в следующем:

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

Здесь для каждого permutations(l) создается новый e1.

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

Здесь для каждого lp2 повторно используется один и тот же e1, но он исчерпывается после первого раза.


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

[…]

Я предполагаю, что это связано с тем, что lp1 и lp2 указывают на один и тот же итератор.

Нет, lp1 и lp2 — разные итераторы.

Другие вопросы по теме