02 июн. 2012 г., 23:35 от user1265125

Как предотвратить истощение итератора в Python (3.x)?

Если я создаю два списка и заархивирую их

a=[1,2,3]
b=[7,8,9]
z=zip(a,b)

Затем я распечатываю z в два списка

l1=list(z)
l2=list(z)

Затем содержимое l1 оказывается нормальным [(1,7), (2,8), (3,9)], но содержимое l2 просто [].

Думаю, это общее поведение python по отношению к итерируемым. Но как начинающий программист, мигрирующий из семейства C, это не имеет смысла для меня. Почему он так себя ведет? И есть ли способ обойти эту проблему?

Я имею в виду, да, в этом конкретном примере я могу просто скопировать l1 в l2, но в целом есть ли способ «сбросить» то, что Python использует для итерации «z» после того, как я повторю один раз?

Ответы на вопрос (0)

03 июн. 2012 г., 13:07 от Thomas Orozco

list() один раз, и используйте его потом.

Бывает так, чтоzip возвращаетgenerator, который являетсяiterator что вы можете повторить только один раз.

Вы можете повторять список столько раз, сколько хотите.

23 мая 2017 г., 13:46 от Communitypepr

анация. Как программист, вы, вероятно, понимаете разницу между классами и экземплярами (т.е. объектами).zip() Говорят, чтоbuilt-in function (в официальном документе). На самом деле, этоbuilt-in generator function, Значит, это скорее класс. Вы даже можете попробовать в интерактивном режиме:

>>> zip
<class 'zip'>

Классы являются типами. Из-за этого также должно быть ясно следующее:

>>> type(zip)
<class 'type'>

Вашz является экземпляром класса, и вы можете подумать о вызовеzip() как насчет вызова конструктора класса:

>>> a = [1, 2, 3]
>>> b = [7, 8, 9]
>>> z = zip(a, b)
>>> z
<zip object at 0x0000000002342AC8>
>>> type(z)
<class 'zip'>

z является итератором (объектом), который хранится внутри итераторов дляa а такжеb, Из-за его общей реализации,z (илиzip класс) не имеет смысла сбрасывать итераторы черезa или жеb или какие-то последовательности. Из-за этого нет способа сброситьz, Самый простой способ решить вашу конкретную проблему - скопировать список (как вы упомянули в вопросе иЛеннарт Регебро предлагает). Другим понятным способом является использованиеzip(a, b) дважды, таким образом построив дваz-подобные итераторы, которые ведут себя с самого начала одинаково:

>>> lst1 = list(zip(a, b))
>>> lst2 = list(zip(a, b))

Тем не менее, это не может быть использовано в целомwith the identical result, Подумать оa или жеb быть уникальными последовательностями, сгенерированными на основе некоторых текущих условий (скажем, температуры считываются с нескольких термометров)

03 июн. 2012 г., 09:13 от Lennart Regebro

Если вам нужны две копии списка, которые вы делаете, если вам нужно изменить их, то я предлагаю вам сделать список один раз, а затем скопировать его:

a=[1,2,3]
b=[7,8,9]
l1 = list(zip(a,b))
l2 = l1[:]
03 июн. 2012 г., 04:02 от Levon

Генераторы генерируют свою продукциюonce, один за другим, по требованию, а затем выполняются, когда выход исчерпан.

Думайте о них, как о прочтении файла, после того как вы закончите, вам придется перезапустить компьютер, если вы хотите еще раз проверить данные.

Если вам нужно сохранить выходные данные генератора, подумайте о том, чтобы сохранить его, например, в списке, и впоследствии использовать его так часто, как вам нужно. (Несколько похоже на решения, которыми руководствовались при использованииxrange()генератор противrange() который создал целый список элементов в памяти в v2)

Обновлено: исправлена терминология, временное отключение мозга ...

03 июн. 2012 г., 15:30 от senderle

can использованиеitertools.tee «копировать» итератор.

>>> z = zip(a, b)
>>> zip1, zip2 = itertools.tee(z)
>>> list(zip1)
[(1, 7), (2, 8), (3, 9)]
>>> list(zip2)
[(1, 7), (2, 8), (3, 9)]

Это включает в себя кэширование значений, поэтому имеет смысл только, если вы повторяете оба итерации с примерно одинаковой скоростью. (Другими словами, не используйте его так, как я здесь!)

Другой подход - обойти функцию генератора и вызывать ее всякий раз, когда вы хотите выполнить итерацию.

def gen(x):
    for i in range(x):
        yield i ** 2

def make_two_lists(gen):
    return list(gen()), list(gen())

Но теперь вы должны привязать аргументы к функции генератора при ее передаче. Ты можешь использоватьlambda для этого, но многие люди находятlambda некрасиво. (Не я, хотя! YMMV.)

>>> make_two_lists(lambda: gen(10))
([0, 1, 4, 9, 16, 25, 36, 49, 64, 81], [0, 1, 4, 9, 16, 25, 36, 49, 64, 81])

Я надеюсь, что само собой разумеется, что в большинстве случаев лучше просто составить список и скопировать его.

Кроме того, как более общий способ объяснить это поведение, рассмотрим это. Задача генератора - создать серию значений, сохраняя некоторое состояние между итерациями. Теперь иногда вместо простой итерации по генератору вы можете захотеть сделать что-то вроде этого:

z = zip(a, b)
while some_condition():
    fst = next(z, None)
    snd = next(z, None)
    do_some_things(fst, snd)
    if fst is None and snd is None:
        do_some_other_things()

Давайте скажем этот циклmay или жеmay not выпускнойz, Теперь у нас есть генератор в неопределенном состоянии! Поэтому на данном этапе важно, чтобы поведение генератора ограничивалось четко определенным образом. Хотя мы не знаем, где находится генератор на его выходе, мы знаем, что a) все последующие обращения приведут кlater значения в серии, и b) после того, как она «пуста», мы получили все элементы в серии ровно один раз. Чем больше у нас возможностей манипулировать состояниемzчем сложнее рассуждать об этом, тем лучше, чтобы мы избегали ситуаций, нарушающих эти два обещания.

Конечно, как Джоэл Корнетт указывает ниже, этоis можно написать генератор, который принимает сообщения черезsend Способ; и было бы возможно написать генератор, который может быть сброшен с помощьюsend, Но учтите, что в этом случаеall we can do is send a message, Мы не можем напрямую манипулировать состоянием генератора, и поэтому все изменения в состоянии генератора четко определены (самим генератором - при условии, что он был написан правильно!).send действительно для реализациисопрограммыпоэтому я бы не использовал его для этой цели. Обычные генераторы почти никогда ничего не делают с отправленными им значениями - я думаю, по тем причинам, которые я привел выше.

ВАШ ОТВЕТ НА ВОПРОС