У меня есть итератор, состоящий из нескольких списков одинакового размера. Для моей цели мне нужно знать длину хотя бы одного из этих списков. Но, как и в случае с итераторами, они не могут быть доступны так же, как обычные массивы. Поэтому моя идея состояла в том, чтобы получить эту длину, сказав:
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. Но да, это длина только одного из списков, которые мне нужны в качестве константы, которую я могу использовать во всем остальном моем коде.
В общем случае итераторы не повторяются, поэтому вам, вероятно, еще нужно будет что-то добавить.
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]
Вы можете сделать небольшой трюк и обернуть исходный итератор в генераторе. Таким образом, вы можете получить первый элемент и «повторно выполнить» его с генератором, не потребляя весь итератор. Функция 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().