Что такое итератор, итерация и итерация?

444

Какое самое основное определение «итерируемого», «итератора» и «итерации» в Python?

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

Может кто-нибудь, пожалуйста, помогите мне с 3 определениями в терминах непрофессионала?

thechrishaddad
источник

Ответы:

531

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

В Python итерируемый и итератор имеют определенные значения.

Итерируемое это объект , который имеет __iter__метод , который возвращает итератор , или который определяет __getitem__метод , который может принимать последовательные индексы , начиная с нуля (и не поднимает , IndexErrorкогда индексы больше не действительны). Таким образом, итерируемый - это объект, от которого вы можете получить итератор .

Итератора является объектом с next(Python 2) или __next__метода (Python 3).

Всякий раз, когда вы используете forцикл, или map, или понимание списка, и т. Д. В Python, nextметод вызывается автоматически для получения каждого элемента от итератора , таким образом, проходя процесс итерации .

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

AGF
источник
1
Обратите внимание, что collections.abc.AsyncIteratorтесты __aiter__и __anext__методы. Это новое дополнение в 3.6.
Янус Троелсен
1
@jlh почему бы __len__обязательно быть привязанным к итерации? Как знание длины чего-либо поможет вам повторить это?
теневик
2
@shadowtalker поможет узнать, какие индексы являются действительными, чтобы вы знали, с какими индексами можно работать __getitem__.
19:06
4
@ jlh звучит так, будто вы предлагаете очень самоуверенное поведение по умолчанию. Предположим, он {'a': 'hi', 'b': 'bye'}имеет длину 2, но не может быть проиндексирован 0, 1 или 2.
shadowtalker
2
@shadowtalker. Но у диктанта есть __iter__метод. Я думаю, что jlh относится к объектам, которые являются итеративными, в частности, потому что они определяют: « __getitem__метод, который может принимать последовательные индексы, начиная с нуля».
Богатые
337

Вот объяснение, которое я использую при обучении на Python:

ITERABLE это:

  • все, что может быть зациклено (то есть вы можете зациклить строку или файл) или
  • все, что может появиться в правой части цикла for: for x in iterable: ... или
  • все, с чем вы можете позвонить iter(), вернет Итератор: iter(obj)или
  • объект, который определяет, __iter__что возвращает свежий ITERATOR, или у него может быть __getitem__метод, подходящий для индексированного поиска.

Итератор - это объект:

  • с состоянием, которое запоминает, где оно находится во время итерации,
  • с помощью __next__метода, который:
    • возвращает следующее значение в итерации
    • обновляет состояние, чтобы указывать на следующее значение
    • сигналы, когда это сделано путем повышения StopIteration
  • и это само итеративно (это означает, что у него есть __iter__метод, который возвращает self).

Ноты:

  • __next__Метод в Python 3 пишется nextв Python 2, и
  • Встроенная функция next()вызывает этот метод переданного ей объекта.

Например:

>>> s = 'cat'      # s is an ITERABLE
                   # s is a str object that is immutable
                   # s has no state
                   # s has a __getitem__() method 

>>> t = iter(s)    # t is an ITERATOR
                   # t has state (it starts by pointing at the "c"
                   # t has a next() method and an __iter__() method

>>> next(t)        # the next() function returns the next value and advances the state
'c'
>>> next(t)        # the next() function returns the next value and advances
'a'
>>> next(t)        # the next() function returns the next value and advances
't'
>>> next(t)        # next() raises StopIteration to signal that iteration is complete
Traceback (most recent call last):
...
StopIteration

>>> iter(t) is t   # the iterator is self-iterable
Раймонд Хеттингер
источник
что вы подразумеваете под свежим итератором?
lmiguelvargasf
13
@lmiguelvargasf «Свежий» как в «новом и неиспользованном», а не «исчерпанный или частично потребленный». Идея состоит в том, что новый итератор начинается в начале, а частично используемый итератор начинает с того места, где он остановился.
Рэймонд Хеттингер
Ваши 2-я, 3-я и 4-я марки четко указывают, что вы имеете в виду, с точки зрения конкретных конструкций Python, встроенных модулей или вызовов методов. Но первая пуля («все, что может быть зациклено») не имеет такой ясности. Кроме того, 1-я пуля, кажется, перекрывается со 2-й, так как 2-я относится к forпетлям, а 1-я относится к "зацикливанию". Не могли бы вы обратиться к этим?
Источник
2
Просьба перефразировать «все, что вы можете позвонить iter()» как «все, что вы можете передатьiter() »
источник
98

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

Кроме того, люди имеют тенденцию становиться «слишком питоническими», помещая такие определения, как «X - это объект, который имеет __foo__() метод» прежде. Такие определения являются правильными - они основаны на философии типизации уток, но при попытке понять концепцию в ее простоте основное внимание уделяется методам.

Поэтому я добавляю свою версию.


На естественном языке,

  • итерация - это процесс одновременного взятия одного элемента в ряд.

В Python

  • итерируемый - это объект, который, иными словами, итерируемый, что проще говоря, означает, что его можно использовать в итерации, например, с forциклом. Как? С помощью итератора . Я объясню ниже.

  • ... в то время как итератор - это объект, который определяет, как на самом деле выполнять итерацию, в частности, что является следующим элементом. Вот почему у него должен быть next()метод.

Сами итераторы также являются итеративными, с той разницей, что их __iter__()метод возвращает один и тот же объект ( self), независимо от того, были ли его элементы использованы предыдущими вызовами next().


Так что же думает интерпретатор Python, когда видит for x in obj:оператор?

Смотри, forпетля. Похоже, работа для итератора ... Давайте возьмем. ... есть этоobj парень, так что давайте спросим его.

"Мистер obj, у вас есть итератор?" (... звонки iter(obj), которые звонки obj.__iter__() , которые радостно раздают блестящий новый итератор _i.)

Хорошо, это было легко ... Давайте начнем итерацию тогда. ( x = _i.next()...x = _i.next() ...)

С мистером obj успешно прошел этот тест (имея определенный метод, возвращающий действительный итератор), мы награждаем его прилагательным: теперь вы можете называть его «итеративный Mr. obj».

Тем не менее, в простых случаях вам не выгодно иметь итератор и итерацию отдельно. Таким образом, вы определяете только один объект, который также является его собственным итератором. (Python на самом деле не волнует, что _iраздаютobj была не такой уж блестящей, а objсама по себе.)

Вот почему в большинстве примеров, которые я видел (и что меня смущало снова и снова), вы можете увидеть:

class IterableExample(object):

    def __iter__(self):
        return self

    def next(self):
        pass

вместо

class Iterator(object):
    def next(self):
        pass

class Iterable(object):
    def __iter__(self):
        return Iterator()

Однако существуют случаи, когда вы можете извлечь выгоду из отделения итератора от итерируемого, например, когда вы хотите иметь один ряд элементов, но больше «курсоров». Например, когда вы хотите работать с «текущими» и «предстоящими» элементами, вы можете иметь отдельные итераторы для обоих. Или несколько потоков, извлекаемых из огромного списка: у каждого может быть свой собственный итератор для обхода всех элементов. Смотрите ответы @ Raymond's и @ glglgl выше.

Представьте, что вы можете сделать:

class SmartIterableExample(object):

    def create_iterator(self):
        # An amazingly powerful yet simple way to create arbitrary
        # iterator, utilizing object state (or not, if you are fan
        # of functional), magic and nuclear waste--no kittens hurt.
        pass    # don't forget to add the next() method

    def __iter__(self):
        return self.create_iterator()

Ноты:

  • Я повторю еще раз: итератор не повторяется . Итератор не может быть использован как «источник» в forцикле. Что в forпервую очередь нужно циклу __iter__() (который возвращает что-то с помощью next()).

  • Конечно, forэто не единственный цикл итерации, поэтому вышеприведенное применимо и к некоторым другим конструкциям ( while...).

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

  • В вышеупомянутом «мыслительном процессе», _iдействительно, не существует. Я придумал это имя.

  • В Python 3.x есть небольшое изменение: next()теперь должен вызываться метод (не встроенный) __next__(). Да, так и должно быть.

  • Вы также можете думать об этом так: итерируемый имеет данные, итератор вытягивает следующий элемент

Отказ от ответственности: я не являюсь разработчиком интерпретатора Python, поэтому я не знаю, что «думает» интерпретатор. Вышеприведенные размышления являются лишь демонстрацией того, как я понимаю эту тему из других объяснений, экспериментов и реального опыта новичка в Python.

Алоис Махдал
источник
1
Это здорово, но я все еще немного растерялся. Я думал, что ваш желтый ящик говорит, что forциклу нужен итератор («Посмотрите, цикл for. Похоже на работу для итератора ... Давайте его получим».). Но затем вы говорите в примечаниях в конце, что «Итератор не может быть использован в качестве источника в forцикле» ...?
Гонки Головастик
Почему вы положили только passв код для этих nextопределений? Я предполагаю, что вы просто имеете в виду, что кто-то должен реализовать способ получить следующий, поскольку next должен что-то возвращать.
nealmcb
@nealmcb Да, я думаю, что это значит для меня. (Это то pass, для чего , в конце концов.)
Алоис
@AloisMahdal Аааа, я не видел такого использования раньше. Когда я вижу pass, я думаю, что это там по синтаксическим причинам. Я просто наткнулся на ответы на объекте с многоточием, которые довольно интересны: вы можете использовать, ...чтобы указать блок «todo позже». NotImplementedтакже доступно.
nealmcb
Хотя мне нравится, что вы подчеркиваете различие между итератором и итератором, этот ответ противоречит сам себе. Сначала вы пишете: «Итераторы сами по себе итерируемы» (что соответствует тому, что написано в документации по Python ). Но потом вы напишите: « Итератор не повторяется . Итератор не может быть использован как «источник» в forцикле. Я понимаю ваш ответ, и мне нравится это в противном случае, но я думаю, что было бы полезно исправить это.
Богатое
22

Итерируемый - это объект, у которого есть __iter__()метод. Он может повторяться несколько раз, например list()s и tuple()s.

Итератор - это объект, который выполняет итерацию. Он возвращается __iter__()методом, возвращает себя через собственный __iter__()метод и имеет next()метод ( __next__()в 3.x).

Итерация - это процесс вызова этого next()соотв. __next__()пока не поднимется StopIteration.

Пример:

>>> a = [1, 2, 3] # iterable
>>> b1 = iter(a) # iterator 1
>>> b2 = iter(a) # iterator 2, independent of b1
>>> next(b1)
1
>>> next(b1)
2
>>> next(b2) # start over, as it is the first call to b2
1
>>> next(b1)
3
>>> next(b1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> b1 = iter(a) # new one, start over
>>> next(b1)
1
glglgl
источник
Так на самом деле это просто объект, который проходит через контейнер? было бы это полезно?
Thechrishaddad
Часто, но не всегда. Курсор генератора, файла или базы данных может быть повторен только один раз и, следовательно, является их собственным итератором.
glglgl
Я думаю, что b2 не должен быть независимым от b1? для этого особого случая он независим, уверен, я могу сделать его не независимым, а также действительным Iterable.
Бен
@ Да, да. Так как и Iteratorвсегда Iterableи является его собственным Iterator, два вызова iter()не обязательно дают два независимых Iterators.
glglgl
13

Вот мой шпаргалка:

 sequence
  +
  |
  v
   def __getitem__(self, index: int):
  +    ...
  |    raise IndexError
  |
  |
  |              def __iter__(self):
  |             +     ...
  |             |     return <iterator>
  |             |
  |             |
  +--> or <-----+        def __next__(self):
       +        |       +    ...
       |        |       |    raise StopIteration
       v        |       |
    iterable    |       |
           +    |       |
           |    |       v
           |    +----> and +-------> iterator
           |                               ^
           v                               |
   iter(<iterable>) +----------------------+
                                           |
   def generator():                        |
  +    yield 1                             |
  |                 generator_expression +-+
  |                                        |
  +-> generator() +-> generator_iterator +-+

Тест: Вы видите, как ...

  1. каждый итератор является итеративным?
  2. контейнерные объекты __iter__()метод может быть реализован как генератор?
  3. итерация, у которой есть __next__метод, не обязательно является итератором?

ответы:

  1. У каждого итератора должен быть __iter__метод. Иметь __iter__достаточно, чтобы быть повторяемым. Поэтому каждый итератор является итеративным.
  2. Когда __iter__вызывается, он должен возвращать итератор ( return <iterator>на диаграмме выше). Вызов генератора возвращает итератор генератора, который является типом итератора.

    class Iterable1:
        def __iter__(self):
            # a method (which is a function defined inside a class body)
            # calling iter() converts iterable (tuple) to iterator
            return iter((1,2,3))
    
    class Iterable2:
        def __iter__(self):
            # a generator
            for i in (1, 2, 3):
                yield i
    
    class Iterable3:
        def __iter__(self):
            # with PEP 380 syntax
            yield from (1, 2, 3)
    
    # passes
    assert list(Iterable1()) == list(Iterable2()) == list(Iterable3()) == [1, 2, 3]
  3. Вот пример:

    class MyIterable:
    
        def __init__(self):
            self.n = 0
    
        def __getitem__(self, index: int):
            return (1, 2, 3)[index]
    
        def __next__(self):
            n = self.n = self.n + 1
            if n > 3:
                raise StopIteration
            return n
    
    # if you can iter it without raising a TypeError, then it's an iterable.
    iter(MyIterable())
    
    # but obviously `MyIterable()` is not an iterator since it does not have
    # an `__iter__` method.
    from collections.abc import Iterator
    assert isinstance(MyIterable(), Iterator)  # AssertionError
AXO
источник
1
В викторине я понял только 1-й пункт. т.е. итератор становится итеративным, так как имеет __iter__метод. Не могли бы вы уточнить 2-й и 3-й пункты, отредактировав этот ответ
AnV
@AnV: насколько я понимаю: re 2 .: __iter__()возвращает итератор. Генератор является итератором, поэтому его можно использовать для этой цели. о 3 .: Я могу только догадываться здесь, но я думаю, что если __iter__()отсутствует или не возвращается self, это не итератор, потому что итератор __iter__()должен вернуться self.
glglgl
10

Я не знаю, помогает ли это кому-либо, но мне всегда нравится визуализировать концепции в моей голове, чтобы лучше понять их. Так как у меня есть маленький сын, я визуализирую концепцию итерации / итератора с кирпичами и белой бумагой.

Предположим, мы в темной комнате и на полу у нас есть кирпичи для моего сына. Кирпичи разного размера, цвета, теперь не имеет значения. Предположим, у нас есть 5 таких кирпичей. Эти 5 кирпичей можно описать как объект - скажем, набор кирпичей . С этим набором кирпичей мы можем многое сделать - взять один, затем второй, третий, поменять местами кирпичи, поставить первый кирпич выше второго. Мы можем сделать много разных вещей с ними. Поэтому этот набор кирпичей является повторяемым объектом или последовательностью, поскольку мы можем пройти через каждый кирпичик и что-то с ним сделать. Мы можем делать это только как мой маленький сын - мы можем играть с одним кирпичиком за раз . Итак, снова я представляю себе этот набор кирпичей, чтобы бытьитеративно .

Теперь помните, что мы в темной комнате. Или почти темно. Дело в том, что мы не можем четко видеть эти кирпичи, какого они цвета, какой формы и т. Д. Поэтому, даже если мы хотим что-то с ними сделать - то есть перебрать их - мы на самом деле не знаем, что и как, потому что это слишком темный.

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

Это, кстати, объясняет мою раннюю ошибку, когда я попробовал следующее в IDLE и получил TypeError:

 >>> X = [1,2,3,4,5]
 >>> next(X)
 Traceback (most recent call last):
    File "<pyshell#19>", line 1, in <module>
      next(X)
 TypeError: 'list' object is not an iterator

Список X здесь был нашим набором кирпичей, но НЕ белым листом бумаги. Сначала мне нужно было найти итератор:

>>> X = [1,2,3,4,5]
>>> bricks_kit = [1,2,3,4,5]
>>> white_piece_of_paper = iter(bricks_kit)
>>> next(white_piece_of_paper)
1
>>> next(white_piece_of_paper)
2
>>>

Не знаю, поможет ли это, но это помогло мне. Если бы кто-то смог подтвердить / исправить визуализацию концепции, я был бы благодарен. Это помогло бы мне узнать больше.

Николай Дудаев
источник
6

Итерируемый : - то, что является итеративным, является итеративным; как последовательности, такие как списки, строки и т. д. Также у него есть __getitem__метод или __iter__метод. Теперь, если мы будем использовать iter()функцию для этого объекта, мы получим итератор.

Итератор : - когда мы получаем объект итератора из iter()функции; мы вызываем __next__()метод (в python3) или просто next()(в python2), чтобы получить элементы один за другим. Этот класс или экземпляр этого класса называется итератором.

Из документов: -

Использование итераторов пронизывает и объединяет Python. За кулисами оператор for вызывает  iter() объект контейнера. Функция возвращает объект итератора, который определяет метод,  __next__() который обращается к элементам в контейнере по одному. Когда элементов больше нет,  __next__() возникает исключение StopIteration, которое сообщает циклу for завершиться. Вы можете вызвать  __next__() метод, используя  next() встроенную функцию; этот пример показывает, как все это работает:

>>> s = 'abc'
>>> it = iter(s)
>>> it
<iterator object at 0x00A1DB50>
>>> next(it)
'a'
>>> next(it)
'b'
>>> next(it)
'c'
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    next(it)
StopIteration

Бывший класс: -

class Reverse:
    """Iterator for looping over a sequence backwards."""
    def __init__(self, data):
        self.data = data
        self.index = len(data)
    def __iter__(self):
        return self
    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]


>>> rev = Reverse('spam')
>>> iter(rev)
<__main__.Reverse object at 0x00A1DB50>
>>> for char in rev:
...     print(char)
...
m
a
p
s
Vicrobot
источник
4

Я не думаю, что вы можете сделать это намного проще, чем документация , однако я попробую:

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

  • Итерация , вероятно, лучше всего объясняется определением слова Мерриам-Вебстер :

б: повторение последовательности компьютерных инструкций указанное количество раз или до тех пор, пока условие не будет выполнено - сравните рекурсию

Kimvais
источник
3
iterable = [1, 2] 

iterator = iter(iterable)

print(iterator.__next__())   

print(iterator.__next__())   

так,

  1. iterableэто объект, который может быть зациклен на . например, список, строка, кортеж и т. д.

  2. использование iterфункции нашего iterableобъекта вернет объект итератора.

  3. теперь у этого объекта итератора есть метод с именем __next__(в Python 3 или просто nextв Python 2), с помощью которого вы можете получить доступ к каждому элементу итерируемого.

Итак, ВЫШЕ ВЫШЕ КОДА БУДУТ:

1

2

Арпан Кумар
источник
3

Итераторы имеют __iter__метод, который каждый раз создает новый итератор.

Итераторы реализуют __next__метод, который возвращает отдельные элементы, и __iter__метод, который возвращает self.

Следовательно, итераторы также являются итеративными, но итераторы не являются итераторами.

Лучано Рамальо, Свободный Питон.

techkuz
источник
2

Прежде чем иметь дело с итераторами и итераторами, основным фактором, определяющим итерируемость и итератор, является последовательность

Последовательность: Последовательность - это сбор данных

Iterable: Iterable - это объект типа последовательности, поддерживающий __iter__метод.

Метод Iter: метод Iter принимает последовательность в качестве входных данных и создает объект, известный как итератор

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

пример:

x=[1,2,3,4]

х последовательность, которая состоит из сбора данных

y=iter(x)

При вызове iter(x)он возвращает итератор только тогда, когда у объекта x есть метод iter, в противном случае он вызывает исключение. Если он возвращает итератор, то y присваивается следующим образом:

y=[1,2,3,4]

Поскольку у является итератором, следовательно, он поддерживает next()метод

При вызове метода next он возвращает отдельные элементы списка по одному.

После возврата последнего элемента последовательности, если мы снова вызываем следующий метод, возникает ошибка StopIteration.

пример:

>>> y.next()
1
>>> y.next()
2
>>> y.next()
3
>>> y.next()
4
>>> y.next()
StopIteration
Тень
источник
Просто наблюдение: y = iter (x) не совсем y = [1,2,3,4], так как y теперь является объектом итератора. Возможно, вам следует добавить комментарий, чтобы уточнить, что это не список, а объект-итератор, или изменить представление.
Coelhudo
-6

В Python все является объектом. Когда говорят, что объект является итеративным, это означает, что вы можете пройти (т.е. итерировать) объект как коллекцию.

Например, массивы являются повторяемыми. Вы можете пройти по ним с помощью цикла for и перейти от индекса 0 к индексу n, где n - длина объекта массива минус 1.

Словари (пары ключ / значение, также называемые ассоциативными массивами) также являются итеративными. Вы можете пройти через их ключи.

Очевидно, что объекты, которые не являются коллекциями, не являются итеративными. Например, объект bool имеет только одно значение: True или False. Он не повторяется (не имеет смысла, что это повторяемый объект).

Читать далее. http://www.lepus.org.uk/ref/companion/Iterator.xml

user93097373
источник
6
объекты, которые не являются коллекциями, не повторяемы, как правило, не так. Чтобы привести только пару примеров, генераторы являются итеративными, но не являются коллекциями, а объекты итераторов, созданные путем вызова iter()стандартных типов коллекций, являются итеративными, но сами по себе не являются коллекциями.
Марк Амери