Ответ 1
'iterator' if obj is iter(obj) else 'iterable'
В Python интерфейс итерации является подмножеством Абстрактные базовые классы. Методы __iter__
в итераторе и итерабеле имеют одно и то же имя, но семантически разные! Поэтому hasattr
бесполезен, но isinstance
обеспечивает чистое решение.
'iterator' if obj is iter(obj) else 'iterable'
Однако существует важная смысловая разница между двумя...
Не очень семантический или важный. Они оба итерабельны - оба они работают с инструкцией 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 )
В большинстве случаев такой "поворот" ваших алгоритмов приводит к программе, которая может быть легче оптимизирована и может быть чистым улучшением производительности.
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:
Эта проклятая машина времени, которую Реймонд заимствовал у Гвидо...
Из-за печати утиной Python,
Любой объект итерабельен, если он определяет, что метод next()
и __iter__()
возвращает себя.
Если сам объект не имеет метода next()
, __iter__()
может возвращать любой объект, который имеет метод next()
Вы можете сослаться на этот вопрос, чтобы увидеть Iterability в Python