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

8

Error: User Rate Limit Exceeded

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

Error: User Rate Limit Exceeded

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

Error: User Rate Limit Exceeded

Error: User Rate Limit Exceeded

Error: User Rate Limit Exceeded

Это поведениеgeneratorsне все повторяемы. Списки, например, являются итеративными, и вы можете вызватьlist(a) и получить копииa столько, сколько вы хотите. Karl Knechtel

Ваш Ответ

5   ответов
2

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

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

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

Это на самом деле не «кастинг», но обычно это лучший подход. Кроме того, вы правильно определили, что итератор это суперкатегория, включающая генераторы и последовательности, так что это мой голос за лучший ответ.
@KarlKnechtel Вы совершенно правы, я редактировал это!
0

анация. Как программист, вы, вероятно, понимаете разницу между классами и экземплярами (т.е. объектами).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 быть уникальными последовательностями, сгенерированными на основе некоторых текущих условий (скажем, температуры считываются с нескольких термометров)

3

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

a=[1,2,3]
b=[7,8,9]
l1 = list(zip(a,b))
l2 = l1[:]
Да, как я уже говорил, яcan просто скопируйте первый список. Я задал этот вопрос только потому, что хотел быть ясным в своих концепциях Python. Спасибо, в любом случае! user1265125
1

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

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

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

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

12

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 действительно для реализациисопрограммыпоэтому я бы не использовал его для этой цели. Обычные генераторы почти никогда ничего не делают с отправленными им значениями - я думаю, по тем причинам, которые я привел выше.

@ user1265125, рассмотри мое недавнее редактирование, которое ответит на твой вопрос более подробно.
Это работает, но сложно и излишне, ИМО. «Не используйте его так, как я». намек на это тоже. :-)
@LennartRegebro, ну я думаюtee существует по уважительной причине, и это самая близкая вещь в стандартных библиотеках, о которой я могу подумать, о функциональности, запрошенной OP. Я предполагаю, что ОП уже знает, что можно копировать список!
Есть такжеsend() особенность.

Похожие вопросы