2016-04-23 8 views
3

Чтобы избежать путаницы, позвольте мне определить:Стандартный способ преобразования функции возврата итератора в «правильную итеративную» функцию возврата?

собственно итерацию: итератор объект, который не является итератор.


Q: Стандартная библиотека DoES Пайтона уже предоставляет возможность преобразовывать «итератор возвращающей функции» в «надлежащей Iterable -returning функции»?


Я думал, что видел это где-то, но теперь я не могу его найти. В частности, я просмотрел документы для itertools, но не заметил.


FWIW, это доморощенный реализация похоже на работу:

def to_iterable_maker(iterator_maker): 

    def iterable_maker(*args, **kwargs): 
     class nonce_iterable(object): 
      def __iter__(self): 
       return iterator_maker(*args, **kwargs) 
     return nonce_iterable() 

    return iterable_maker 

... но одноразовый nonce_iterable класс там выглядит неуклюжим для меня. Я уверен, что реализация такой вещи из стандартной библиотеки будет намного лучше.


@Nikita

Попробуйте это:

import itertools 

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) 
# [] 

myperms = to_iterable_maker(itertools.permutations) 
keeps_going = myperms(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)] 

Там есть разница между значениями, возвращенными itertools.permutations и to_iterable_maker(itertools.permutations). Мой вопрос: стандартная библиотека уже предоставляет что-то аналогичное to_iterable_maker?

+0

Вы, кажется, запутался , Все итераторы в python являются итерабельными. Тот факт, что метод '__iter__' является функцией идентификации, не имеет значения. Вы пытаетесь сделать итерабельность, которая ** повторяется более одного раза **? – ppperry

+3

Есть ли конкретная проблема, которую вы пытаетесь решить с этим? – jonrsharpe

+0

Ваш «итеративный создатель» будет снова и снова запускать оригинальный итератор. В качестве альтернативы, возможно, вы можете использовать ['itertools.tee'] (https://docs.python.org/3/library/itertools.html#itertools.tee)? –

ответ

0

Не имеет смысла - «функция возврата итератора» в «итерируемую функцию возврата». Если функция возвращает итератор, то она уже возвращает итерабельность, потому что итераторы являются итерабельными, поскольку они должны иметь метод __iter__.

От the docs:

итерацию

Объект может возвращать его члены по одному. Примеры итераций включают в себя все типы последовательностей (такие как список, строка и кортеж) и некоторые типы без последовательности, такие как dict, файловые объекты и объекты любых классов, которые вы определяете с помощью метода __iter__() или __getitem__(). Итераторы могут использоваться в цикле for и во многих других местах, где необходима последовательность (zip(), map(), ...). Когда итерируемый объект равен , переданному как аргумент встроенной функции iter(), он возвращает итератор для объекта. Этот итератор хорош для одного прохода над набором значений . При использовании итераций обычно не требуется вызывать iter() или самостоятельно обрабатывать объекты итератора.Оператор for делает это автоматически для вас, создавая временную неназванную переменную , чтобы удерживать итератор в течение всего цикла. См. Также итератор, последовательность и генератор.

итератора

Объект, представляющий поток данных. Повторные вызовы метода итератора __next__() (или передача его во встроенную функцию next()) возвращают последовательные элементы в потоке. Когда больше нет данных , вместо этого возникает исключение StopIteration. На этом этапе объект итератора исчерпан, и любые дальнейшие вызовы его метода __next__() просто снова вызовут StopIteration.

итераторов должны иметь метод __iter__(), который возвращает сам объект итератора так что каждый итератора также итерируемый

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

UPD:

Что я имею в виду ...

(отображены шагами для сравнения)

Case 1:

  1. f = to_iterable_maker(iterator_maker);
  2. i = f(some_var), i является nonce_iterable с __iter__;
  3. j = iter(i), j Итератор возвращается iterator_maker(some_var);
  4. next(j), возвращает некоторое значение, зависящее от some_var.

Случай 2:

  1. f = iterator_maker;
  2. i = f(some_var), i - итератор, равный iterator_maker(some_var), который имеет __iter__ (по протоколу итератора);
  3. j = iter(i), j является итератор, возвращаемый iterator_maker(some_var), потому что вызов __iter__ на итератора возвращает себя, так j is i возвращает true;
  4. 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)] 
+0

По предложению DSM я обновил свой вопрос, чтобы «обновить» сноску оригинала до тела сообщения. Пожалуйста, прочитайте это обновление. Также см. Код, который я предоставил. Даже если вы возражаете против формулировки моего вопроса, код, который я вам предоставил, недвусмыслен. Вопрос сводится к следующему: имеет ли стандартная библиотека уже функция, выполняющая то, что делает 'to_iterable_maker', или поддерживает эту функцию каким-то другим способом? – kjo

+0

@kjo, как я вижу, вы пытаетесь обернуть итератор, чтобы сделать его итерируемым. Пожалуйста, см. Обновление ответа. – Nikita

+0

Я редактировал свой пост только для вас. – kjo