Перезапустить цикл for после перерыва

У меня есть итератор, состоящий из нескольких списков одинакового размера. Для моей цели мне нужно знать длину хотя бы одного из этих списков. Но, как и в случае с итераторами, они не могут быть доступны так же, как обычные массивы. Поэтому моя идея состояла в том, чтобы получить эту длину, сказав:

for i in iter:
    list_len = len(i)
    break

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

Есть ли способ исправить это? Или, что такое пифонический способ сделать это? Я думал / читал об этом:

from itertools import tee

iter_tmp, iter = tee(iter)
for i in iter_tmp:
    list_len = len(i)
    break

И да, это тоже работает, так как теперь я могу использовать оригинальный iter для последующего использования, но мне просто больно видеть, что у меня есть сделать цикл, импортировать itertools и так просто получить длину списка в итераторе.

UPDATE

Просто пытаюсь объяснить, что я делаю.

Поскольку такие итерации не являются список или массив, но в моем случае, если бы я должен был пропустить мой итератор, я бы получил что-то вроде (в случае моего итератора с четырьмя «списками» в нем):

>>> for i in iter_list:
        print(i)
[1, 2, 5, 3]
[3, 2, 5, 8]
[6, 8, 3, 7]
[1, 4, 6, 1]

Теперь все «списки» в итераторе имеют одинаковую длину, но поскольку сами списки вычисляются с помощью многих шагов, я действительно не знаю длины до того, как она войдет в итератор. Если я не использую итератор, у меня заканчивается память - так это решение pro / con. Но да, это длина только одного из списков, которые мне нужны в качестве константы, которую я могу использовать во всем остальном моем коде.

1
задан 13 August 2018 в 15:57

2 ответа

В общем случае итераторы не повторяются, поэтому вам, вероятно, еще нужно будет что-то добавить.

class peek_iterator(object):
    def __init__(self, source):
        self._source = iter(source)
        self._first = None
        self._sent = False

    def __iter__(self):
        return self

    def next(self):
        if self._first is None:
            self._first = self._source.next()
        if self._sent:
            return self._source.next()
        self._sent = True
        return self._first

    def get_isotropic(self, getter):
        if self._first is None:
            self._first = self._source.next()
        return getter(self._first)

lists = [[1, 2, 3], [4, 5, 6]]

i = peek_iterator(lists)

print i.get_isotropic(len) # 3

for j in i: print j        # [1, 2, 3]; [4, 5, 6]
0
ответ дан 15 August 2018 в 17:00

Вы можете сделать небольшой трюк и обернуть исходный итератор в генераторе. Таким образом, вы можете получить первый элемент и «повторно выполнить» его с генератором, не потребляя весь итератор. Функция head() ниже возвращает первый элемент и генератор, который выполняет итерацию по исходной последовательности.

def head(seq):
    seq_iter = iter(seq)
    first = next(seq_iter)
    def gen():
        yield first
        yield from seq_iter
    return first, gen()

seq = range(100, 300, 50)
first, seq2 = head(seq)

print('first item: {}'.format(first))

for item in seq2:
    print(item)

Выход:

first item: 100
100
100
150
200
250

Это концептуально эквивалентно ответу Моберга, но использует генератор для «повторной сборки» исходной последовательности вместо itertools.chain().

0
ответ дан 15 August 2018 в 17:00

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

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