Как использовать итератор несколько раз в python - PullRequest
0 голосов
/ 30 марта 2020

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

   #data
   fruit= ("grape", "banana", "apple")
   #iterator
   myit = iter(fruit)

   #the function I have
   def printIter(its):
     for x in its:
        print(x)

   def printIter2(its):
     for x in its:
        print(x)

Я должен дважды вызвать printIter на итераторе, но он должен выполнять совершенно разные функции. Но итератор может быть использован только один раз. У меня нет контроля над источником данных fruit и итератором myit. У меня есть только контроль над функциями printIter().

Как лучше всего достичь своей цели, используя меньше памяти.

Что у меня сейчас есть:

   it1, it2 = itertools.tee(its)
   printIter(it1)
   printIter(it2)
   del it1, it2

Это хорошая практика, как-нибудь иначе?

Ответы [ 2 ]

1 голос
/ 30 марта 2020

Если у вас есть только итератор, и вам нужно выполнить два вида обработки на нем, не занимая слишком много памяти, лучше всего спроектировать обработку, которую вы выполняете для параллельной работы. То есть вы должны иметь возможность выполнять обе части обработки по одному элементу за раз. В вашем примере обе ваши итераторские функции просто распечатывали его, что не очень хорошо подходит для распараллеливания (вы получите распечатку в другом порядке, например 1, 1, 2, 2, 3, 3, ...). Но для других типов проблем легко выполнить часть работы, а затем ждать больше данных.

Вот пример, где я использую две функции генератора для параллельного использования итератора tee (используя встроенный zip). Один складывает полученные значения и печатает только окончательную сумму, а другой печатает их индивидуально.

def consume1(it):
    total = 0
    for value in it:
        total += value
        yield
    print(total)

def consume2(it):
    for value in it:
        print(value)
        yield

opaque_iterator = iter((1, 2, 3, 4))
it1, it2 = itertools.tee(opaque_iterator)

for _ in zip(consume1(it1), consume2(it2)):
    pass

Вывод:

1
2
3
4
10

Существует множество тонкостей такого рода кода, так что не удивляйтесь, если вы не получите его с первой попытки. Мой код выше выглядит как agile, так как zip на самом деле не предназначен для управления такими генераторами, как этот.

0 голосов
/ 30 марта 2020

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

Однако, если вы не хотите, чтобы накладные расходы памяти двух копий итератора выполнялись одновременно, как это происходит с tee(), вы можете просто переопределить итератор после того, как первый будет использован и удален

import itertools

#the function I have
def printIter(its):
  for x in its:
    print(x)

def printIter2(its):
  for x in its:
    print(x)


#data
fruit= ("grape", "banana", "apple")

#iterator
myit = iter(fruit)

#it1, it2 = itertools.tee(myit)
printIter(myit)
del myit
myit = iter(fruit)
printIter2(myit)
del myit

Поскольку вы указали, что у вас нет доступа к исходным данным, tee(), вероятно, лучшее, что вы можете сделать с итераторами. Однако вы можете рассмотреть возможность преобразования одного итератора в список, а затем выполнить повторяющиеся операции над этим.

import itertools
#data
fruit= ("grape", "banana", "apple")
#iterator
myit = iter(fruit)

def printIter(its):
  for x in its:
    print(x)

mylist = list(myit)
del myit
printIter(mylist)
printIter(mylist)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...