Не имеет смысла - «функция возврата итератора» в «итерируемую функцию возврата». Если функция возвращает итератор, то она уже возвращает итерабельность, потому что итераторы являются итерабельными, поскольку они должны иметь метод __iter__
.
От the docs:
итерацию
Объект может возвращать его члены по одному. Примеры итераций включают в себя все типы последовательностей (такие как список, строка и кортеж) и некоторые типы без последовательности, такие как dict, файловые объекты и объекты любых классов, которые вы определяете с помощью метода __iter__
() или __getitem__
(). Итераторы могут использоваться в цикле for и во многих других местах, где необходима последовательность (zip(), map(), ...). Когда итерируемый объект равен , переданному как аргумент встроенной функции iter(), он возвращает итератор для объекта. Этот итератор хорош для одного прохода над набором значений . При использовании итераций обычно не требуется вызывать iter() или самостоятельно обрабатывать объекты итератора.Оператор for делает это автоматически для вас, создавая временную неназванную переменную , чтобы удерживать итератор в течение всего цикла. См. Также итератор, последовательность и генератор.
итератора
Объект, представляющий поток данных. Повторные вызовы метода итератора __next__
() (или передача его во встроенную функцию next()) возвращают последовательные элементы в потоке. Когда больше нет данных , вместо этого возникает исключение StopIteration. На этом этапе объект итератора исчерпан, и любые дальнейшие вызовы его метода __next__
() просто снова вызовут StopIteration.
итераторов должны иметь метод __iter__
(), который возвращает сам объект итератора так что каждый итератора также итерируемый
и может быть использован в большинстве мест, где принимаются другие итерируемыми. Одним из примечательных исключений является код, который пытается выполнить несколько итераций. Объект-контейнер (например, список) создает новый новый итератор каждый , когда вы передаете его функции iter() или используете его в цикле for. Попытка этого с помощью итератора будет возвращать тот же исчерпанный объект итератора , который использовался в предыдущем проходе итерации, заставляя его отображаться как пустой контейнер.
UPD:
Что я имею в виду ...
(отображены шагами для сравнения)
Case 1:
f = to_iterable_maker(iterator_maker)
;
i = f(some_var)
, i
является nonce_iterable
с __iter__
;
j = iter(i)
, j
Итератор возвращается iterator_maker(some_var)
;
next(j)
, возвращает некоторое значение, зависящее от some_var
.
Случай 2:
f = iterator_maker
;
i = f(some_var)
, i
- итератор, равный iterator_maker(some_var)
, который имеет __iter__
(по протоколу итератора);
j = iter(i)
, j
является итератор, возвращаемый iterator_maker(some_var)
, потому что вызов __iter__
на итератора возвращает себя, так j is i
возвращает true
;
next(j)
, возвращает некоторое значение, зависящее от some_var
.
Как вы можете видеть, ничего не меняется, кроме дополнительных осложнений на этапе подготовки.
Возможно, вы могли бы предоставить дополнительную информацию о том, чего вы пытаетесь достичь такой «упаковкой», чтобы понять реальную проблему.
В соответствии с вашим вопросом я не могу придумать какую-либо библиотечную функцию, которая превратила бы итератор в итерируемый, потому что он уже есть. Если вы пытаетесь дублировать итераторы, можете взглянуть на itertools.tee()
.
UPD2:
Итак, теперь я вижу, что цель состоит в том, чтобы преобразовать один проход итератор многоходовой итератор ...
Мой ответ:
«Стандартная библиотека уже предоставляет что-то аналогичное to_iterable_maker?»
«Нет». Но ближайший - itertools.tee()
, который может помочь вам клонировать один итератор в несколько, которые вы можете использовать после. Что касается вашего примера:
import itertools
base = range(3)
poops_out = itertools.permutations(base)
iterators = itertools.tee(poops_out, 4)
#You shouldn't use original iterator after clonning, so make it refer to a clone
#to be used again, otherwise ignore the following line
poops_out, iterators = iterators[0], iterators[1:]
for it in iterators:
print list(it)
#Prints:
#[(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
#[(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
#[(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
Другой распространенный способ получить Iterable от итератора, чтобы преобразовать его с помощью list()
или tuple()
, что позволит многоходовой:
import itertools
base = range(3)
poops_out = itertools.permutations(base)
#Obviously poops_out gets consumed at the next line, so it won't iterate anymore
keeps_going = tuple(poops_out)
print list(poops_out)
# []
print list(poops_out)
# []
print list(keeps_going)
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
print list(keeps_going)
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
print list(keeps_going)
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
Оба описанных выше методы могут быть тяжелы на использование памяти, поэтому иногда это не вариант. В таком случае решение, к которому вы пришли, будет работать хорошо. Другой реализация, которую я мог думать, и который является немного более объектно-ориентированным, но в остальном не сильно отличается от вашей:
class IterableMaker(object):
'''Wraps function returning iterator into "proper" iterable'''
def __init__(self, iterator_maker):
self.f = iterator_maker
def __call__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
return self
def __iter__(self):
return self.f(*self.args, **self.kwargs)
использование тот же:
import itertools
class IterableMaker(object):
def __init__(self, iterator_maker):
self.f = iterator_maker
def __call__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
return self
def __iter__(self):
return self.f(*self.args, **self.kwargs)
base = range(3)
poops_out = itertools.permutations(base)
print list(poops_out)
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
print list(poops_out)
# []
my_perm = IterableMaker(itertools.permutations)
keeps_going = my_perm(base)
print list(keeps_going)
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
print list(keeps_going)
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
print list(keeps_going)
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
Вы, кажется, запутался , Все итераторы в python являются итерабельными. Тот факт, что метод '__iter__' является функцией идентификации, не имеет значения. Вы пытаетесь сделать итерабельность, которая ** повторяется более одного раза **? – ppperry
Есть ли конкретная проблема, которую вы пытаетесь решить с этим? – jonrsharpe
Ваш «итеративный создатель» будет снова и снова запускать оригинальный итератор. В качестве альтернативы, возможно, вы можете использовать ['itertools.tee'] (https://docs.python.org/3/library/itertools.html#itertools.tee)? –