Как определить разницу между итератором и итерабельностью?

В Python интерфейс итерации является подмножеством Абстрактные базовые классы. Методы __iter__ в итераторе и итерабеле имеют одно и то же имя, но семантически разные! Поэтому hasattr бесполезен, но isinstance обеспечивает чистое решение.

Ответ 1

'iterator' if obj is iter(obj) else 'iterable'

Ответ 2

Однако существует важная смысловая разница между двумя...

Не очень семантический или важный. Они оба итерабельны - оба они работают с инструкцией for.

Разница, например, важна, когда нужно несколько циклов.

Когда это когда-нибудь появляется? Вы должны быть более конкретными. В редких случаях, когда вам нужно сделать два прохода через итерируемую коллекцию, часто есть лучшие алгоритмы.

Например, скажем, вы обрабатываете список. Вы можете перебирать список, который вам нужен. Почему вы запутались с итератором вместо iterable? Ладно, это не сработало.

Хорошо, вот один. Вы читаете файл за два прохода, и вам нужно знать, как reset iterable. В этом случае это файл, и требуется seek; или закрыть и снова открыть. Это неприятно. Вы можете readlines получить список, который допускает два прохода без какой-либо сложности. Так что не нужно.

Подождите, что, если у нас есть такой большой файл, мы не можем все это прочитать в памяти? И, по неясным причинам, мы тоже не можем искать. Что тогда?

Теперь мы спустились на два прохода. На первом проходе мы что-то накопили. Индекс или сводка или что-то еще. Индекс имеет все данные файла. Резюме, часто, является реструктуризацией данных. С небольшим изменением от "резюме" до "реструктуризации" мы сохранили данные файла в новой структуре. В обоих случаях нам не нужен файл - мы можем использовать индекс или сводку.

Все "двухпроходные" алгоритмы могут быть изменены на один проход исходного итератора или итерабельного и второго прохода другой структуры данных.

Это не LYBL или EAFP. Это алгоритм. Вам не нужно reset итератор - YAGNI.


Edit

Вот пример итератора/итерабельной проблемы. Это просто плохо разработанный алгоритм.

it = iter(xrange(3))
for i in it: print i,; #prints 1,2,3 
for i in it: print i,; #prints nothing

Это тривиально фиксировано.

it = range(3)
for i in it: print i
for i in it: print i

"Несколько раз параллельно" тривиально фиксировано. Напишите API, который требует итерации. И когда кто-то отказывается читать документацию API или отказывается следовать ему после прочтения, их материал ломается. Как и должно быть.

"Приятно защищать от случая, когда пользователь предоставляет только итератор, когда требуется несколько проходов" - оба примера безумных людей, пишущих код, который нарушает наш простой API.

Если кто-то достаточно сумасшедший, чтобы читать большинство (но не весь документ API) и предоставлять итератор, когда требуется итерация, вам нужно найти этого человека и научить его (1), как читать всю документацию API и (2) следуйте документации API.

Проблема "защиты" не очень реалистична. Эти сумасшедшие программисты удивительно редки. И в тех немногих случаях, когда это происходит, вы знаете, кто они и могут им помочь.


Изменить 2

"Мы должны прочитать одну и ту же структуру несколько раз" алгоритмы являются фундаментальной проблемой.

Не делайте этого.

for element in someBigIterable:
    function1( element )
for element in someBigIterable:
    function2( element )
...

Сделайте это, вместо этого.

for element in someBigIterable:
    function1( element )
    function2( element )
    ...

Или, рассмотрите что-то вроде этого.

for element in someBigIterable:
    for f in ( function1, function2, function3, ... ):
        f( element )

В большинстве случаев такой "поворот" ваших алгоритмов приводит к программе, которая может быть легче оптимизирована и может быть чистым улучшением производительности.

Ответ 3

import itertools

def process(iterable):
    work_iter, backup_iter= itertools.tee(iterable)

    for item in work_iter:
        # bla bla
        if need_to_startover():
            for another_item in backup_iter:

Эта проклятая машина времени, которую Реймонд заимствовал у Гвидо...

Ответ 4

Из-за печати утиной Python,

Любой объект итерабельен, если он определяет, что метод next() и __iter__() возвращает себя.

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

Вы можете сослаться на этот вопрос, чтобы увидеть Iterability в Python