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

В Python интерфейс итерабельного является подмножеством интерфейса итератора . Это имеет то преимущество, что во многих случаях их можно рассматривать одинаково. Однако между ними существует важная смысловая разница, поскольку для итеративного __iter__ возвращается новый объект итератора, а не только self . Как я могу проверить, что итерабельность действительно является итерабельным, а не итератором? Концептуально я понимаю, что iterables – это коллекции, в то время как итератор управляет итерацией (т. Е. Отслеживает позицию), но не является самой коллекцией.

Разница, например, важна, когда нужно несколько циклов. Если итератор задан, то второй цикл не будет работать, так как итератор уже был использован и напрямую вызывает StopIteration .

Заманчиво тестировать next метод, но это кажется опасным и как-то неправильным. Должен ли я просто проверить, что второй цикл был пуст?

Есть ли способ сделать такой тест более питоническим способом? Я знаю, что это звучит как классический случай LBYL против EAFP, так что, может быть, я просто должен отказаться? Или я чего-то не хватает?

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

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

Изменить 2: На самом деле это очень хороший пример для базовых базовых классов . Методы __iter__ в итераторе и итерабеле имеют одно и то же имя, но семантически разные! Так hasattr бесполезен, но isinstance обеспечивает чистое решение.

  • Есть ли эффективный и быстрый способ загрузки больших json-файлов в python?
  • TypeError: '<=' не поддерживается между экземплярами 'str' и 'int'
  • Разрешить Ctrl-C прерывать C-расширение python
  • Уменьшение серии pandas с несколькими значениями nan до набора дает несколько значений nn
  • В интерпретаторе Python вернитесь без "" "
  • поиск элементов по атрибуту с помощью lxml
  • Получить строку Номер определенной фразы в файле Python
  • Добавление ManyToManyWidget в обратную сторону ManyToManyField в Django Admin
  • 4 Solutions collect form web for “Как определить разницу между итератором и итерабельностью?”

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

    Тем не менее, существует важная смысловая разница между двумя …

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

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

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

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

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

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

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

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

    Это не LYBL или EAFP. Это алгоритм. Вам не нужно перезагружать итератор – YAGNI.


    редактировать

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

     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 ) 

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

     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: 

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

    Из-за того,

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

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

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

    Python - лучший язык программирования в мире.