В 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