Що саме ітератор, ітератор та ітерація?


442

Яке найосновніше визначення поняття "ітерабельність", "ітератор" та "ітерація" в Python?

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

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

Відповіді:


530

Ітерація - це загальний термін прийняття кожного предмета, один за одним. Кожен раз, коли ви використовуєте цикл, явний або неявний, для переходу через групу елементів, тобто ітерація.

У Python iterable та iterator мають конкретні значення.

Ітеріруемое це об'єкт , який має __iter__метод , який повертає ітератор , або який визначає __getitem__метод , який може приймати послідовні індекси , починаючи з нуля (і не піднімає , IndexErrorколи індекси більше не дійсні). Тож ітерабельний об’єкт, з якого можна отримати ітератор .

Ітератора є об'єктом з next(Python 2) або __next__методу (Python 3).

Кожного разу, коли ви використовуєте forцикл, або map, або розуміння списку, тощо в Python, nextметод викликається автоматично, щоб отримати кожен елемент з ітератора , таким чином, проходячи процес ітерації .

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


1
Зауважте, що collections.abc.AsyncIteratorтести для __aiter__та __anext__методи. Це нове доповнення в 3.6.
Янус Троельсен

1
@jlh чому б __len__це обов'язково було пов'язано з ітерацією? Як би знання довжини чогось допомогло вам перебрати це?
shadowtalker

2
@shadowtalker це допоможе дізнатися, які індекси дійсні, тому ви знаєте, з якими індексами можна використовувати __getitem__.
jlh

4
@jlh, це здається, що ви пропонуєте дуже впевнену поведінку. Вважайте, що {'a': 'hi', 'b': 'bye'}довжина має 2, але не може бути індексована 0, 1 або 2.
shadowtalker

2
@shadowtalker. Але диктант має __iter__метод. Я думаю, що jlh має на увазі об'єкти, які спеціально ітерабельні, тому що вони визначають: " __getitem__метод, який може приймати послідовні індекси, починаючи з нуля".
Багатий

337

Ось пояснення, яке я використовую при викладанні класів Python:

ІТЕРАБІЛЬНИЙ:

  • все, що може бути перекинуто на цикл (тобто ви можете перевести цикл на рядок або файл) або
  • все, що може з’явитися з правого боку циклу for: for x in iterable: ...або
  • все, що ви можете зателефонувати з iter()цим, поверне ІТЕРАТОР: iter(obj)або
  • об'єкт, який визначає, __iter__що повертає свіжий ІТЕРАТОР, або він може мати __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
Pls розглянути повторно формулюючи «що - небудь ваш може зателефонувати з iter()» як «все , що ви можете перейти до iter()»
Джерела

98

Наведені вище відповіді чудові, але, як і більшість того, що я бачив, не підкреслюйте різницю достатньо для таких людей, як я.

Крім того, люди схильні отримувати "занадто піфонічний", ставлячи раніше визначення "X - це об'єкт, у якого є __foo__()метод". Такі визначення є правильними - вони базуються на філософії типізації качок, але фокус на методах має тенденцію розбиратися, намагаючись зрозуміти поняття в його простоті.

Тому я додаю свою версію.


Природною мовою,

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

У Python,

  • iterable - це об’єкт, який, ну, ітерабельний, що, просто кажучи, означає, що його можна використовувати в ітерації, наприклад, з 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досяг успіху в цьому тесті (маючи певний метод повернення дійсного ітератора), ми винагороджуємо його прикметником: тепер ви можете назвати його "містер, який можна отримати 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 та @ 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циклі" ...?
Гонка на стовпчику

Чому ви просто passвводите код у ці nextвизначення? Я припускаю, що ви просто маєте на увазі, що хтось повинен реалізувати спосіб отримати наступний, оскільки наступний повинен щось повернути.
nealmcb

@nealmcb Так, я думаю, що це означало минуле мене. (Це те , що passє для , в кінці кінців.)
Alois Mahdal

@AloisMahdal Ааа, я раніше цього не бачив. Коли я бачу pass, я думаю, що це там із синтаксичних причин. Я просто натрапив на відповіді на об’єкт еліпсису, які є досить цікавими: ви можете використовувати ...для позначення блоку "Тодо пізніше". 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

Тож справді це просто предмет, який проходить через контейнер? було б це корисно?
thechrishaddad

Часто, але не завжди. Курсор генератора, файлу або бази даних може бути повторений лише один раз і, таким чином, є їх власними ітераторами.
glglgl

Я думаю, що b2 не повинен незалежно від b1? для цього особливого випадку це незалежно, я впевнений, що можу зробити його не незалежним, а й дійсним Iterable.
Бін

@Bin Так. Як і Iteratorзавжди є, так Iterableі є власним Iterator, два дзвінки iter()не обов'язково дають два незалежні Iteratorсистеми.
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

1
У вікторині я зрозумів лише 1-й пункт кулі. тобто ітератор стає ітерабельним, як і __iter__метод. Чи можете ви детальніше зупинитися на другому та третьому пунктах, відредагувавши цю відповідь
AnV

@AnV: Наскільки я розумію: re 2 .: __iter__()повертає ітератор. Генератор - ітератор, тому його можна використовувати для цієї мети. re 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, який повідомляє циклу для завершення. Ви можете викликати  __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

4

Я не думаю, що ви можете отримати його набагато простіше, ніж документацію , проте спробую:

  • Ітерабельність - це те, що можна повторити . На практиці це зазвичай означає послідовність, наприклад, щось, що має початок і кінець, і певний спосіб пройти всі елементи в ньому.
  • Ви можете вважати Iterator як хелпер-псевдометод (або псевдоатрибут), який дає (або утримує) наступний (або перший) елемент в ітерабелі . (На практиці це лише об'єкт, який визначає метод next())

  • Ітерація , ймовірно , краще за все пояснюється Merriam-Webster визначення слова :

b: повторення послідовності інструкцій на комп’ютері задану кількість разів або до досягнення умови - порівняйте рекурсію


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.

Отже, ітератори також ітерабельні, але ітератори - це не ітератори.

Лучано Рамальо, вільний питон.


2

Перш ніж розбиратися з ітераторами та ітератором, головним фактором, який визначає ітератор та ітератор, є послідовність

Послідовність: Послідовність - це збір даних

Iterable: Iterable - це об'єкт типу послідовності, який підтримує __iter__метод.

Метод Iter: метод Iter приймає послідовність як вхід і створює об'єкт, відомий як ітератор

Ітератор: Ітератор - це об'єкт, який викликає наступний метод і проходить через послідовність. Після виклику наступного методу він повертає об'єкт, який він пройшов в даний момент.

приклад:

x=[1,2,3,4]

x - послідовність, яка складається з збору даних

y=iter(x)

При виклику iter(x)він повертає ітератор лише тоді, коли у об'єкта x є метод iter, інакше він створює виняток. Якщо він повертає ітератор, то y призначається так:

y=[1,2,3,4]

Оскільки y - ітератор, отже, це 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, довжина об'єкта масиву мінус 1.

Словники (пари ключ / значення, які також називаються асоціативними масивами) також є ітерабельними. Ви можете переступити через їх ключі.

Очевидно, що об'єкти, які не є колекціями, не можна відшукати. Наприклад, об'єкт bool має лише одне значення, істинне або помилкове. Це не ітерабельно (не було б сенсу, що це об’єкт, який можна відшукати).

Детальніше http://www.lepus.org.uk/ref/companion/Iterator.xml


6
об'єкти, які не є колекціями, не є ітерабельними, як правило, не відповідають дійсності. Наводимо лише декілька прикладів, генератори є ітерабельними, але не є колекціями, а об’єкти ітератора, створені за допомогою виклику iter()стандартних типів колекцій, є ітерабельними, але самі по собі не є колекціями.
Марк Амері
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.