Сообщить об ошибке.

Тип данных Iterator, протокол итератора в Python

Содержание:


Понятие итератор.

Тип iterator является обобщением понятия последовательности. Объект считается итерируемым, если он физически является последовательностью, либо он является объектом, который воспроизводит по одному результату за раз в контексте инструментов выполнения итераций, таких как цикл for ... in.

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

Недостаток типа Iterator состоит в том, что при первом его вызове вычисляются сразу все значения последовательности как физической, так и виртуальной, к тому же все они хранятся в памяти до их исчерпания. Этот недостаток решает тип generator (генератор).

Чтобы обеспечить поддержку итерации, для объектов контейнера необходимо обязательно определить один метод container.__iter__(). Этот метод возвращает объект итератора. Объект необходим для поддержки протокола итератора, описанного ниже. Если контейнер поддерживает различные типы итераций, можно предоставить дополнительные методы для конкретного запроса итераторов для этих типов итераций.

Примером объекта, поддерживающего несколько форм итерации, может служить древовидная структура, поддерживающая обход как по ширине, так и по глубине. Метод container.__iter__() соответствует слоту tp_iter структуры типа для объектов Python в Python/C API.

Ограничения итератора.

  • Нельзя получить длину итератора функцией len();

    >>> it = iter([i*i for i in range(10)])
    >>> len(it)
    # Traceback (most recent call last):
    #   File "<stdin>", line 1, in <module>
    # TypeError: object of type 'list_iterator' has no len()
    
  • Итератор не поддерживает получение элемента по индексу;

    >>> it = iter([i*i for i in range(10)])
    >>> it[2]
    # Traceback (most recent call last):
    #   File "<stdin>", line 1, in <module>
    # TypeError: 'list_iterator' object is not subscriptable
    
  • К итератору нельзя применить обычные операции среза или функцию slice(). Для этих целей, можно использовать функцию itertools.islice() модуля itertools.

    >>> it = iter([i*i for i in range(10)])
    >>> it[2:5]
    # Traceback (most recent call last):
    #   File "<stdin>", line 1, in <module>
    # TypeError: 'list_iterator' object is not subscriptable
    >>> import itertools
    >>> list(itertools.islice(it, 2, 5))
    # [49, 64, 81]
    
  • После прохождения по итератору, он остается пустым;

    >>> it = iter([i*i for i in range(10)])
    >>> list(it)
    # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    >>> list(it)
    # []
    

Реализация/протокол типа Iterator:

Объекты типа Iterator должны поддерживать следующие два метода, которые вместе образуют протокол итератора :

container.__iter__():

Метод вернет объект итератора. Это необходимо для того, чтобы разрешить использование контейнеров и итераторов с операторами for и in. Метод container.__iter__() соответствует слоту tp_iter структуры типа для объектов Python в API-интерфейсе. PyTypeObject.tp_iter - необязательный указатель на функцию, которая возвращает итератор для объекта. Его присутствие обычно сигнализирует о том, что экземпляры этого типа являются итеративными, хотя последовательности могут быть итерируемыми без этой функции.

container.__next__():

Метод должен возвращать следующий элемент из контейнера, а если элементы в последовательности закончились, то метод container.__next__() должен бросить исключение StopIteration.

Python определяет несколько объектов итератора для поддержки итерации по общим и конкретным типам последовательностей, словарям и другим более специализированным формам. Конкретные типы не важны за пределами их реализации протокола итератора.

Как только метод container.__next__() итератора вызывает StopIteration, он должен продолжать вызывать его и при последующих вызовах. Реализации, которые не подчиняются этому свойству, считаются нарушенными.

Для создания объекта типа Iterator можно воспользоваться встроенной функцией iter(). По итератору можно двигаться с помощью функции next().

Пример создания итератора Iterator:

class SimpleIterator:
    def __iter__(self):
        return self

    def __init__(self, limit):
        self.limit = limit
        self.counter = 0

    def __next__(self):
        if self.counter < self.limit:
            ret = self.counter
            self.counter += 1
            return ret
        else:
            raise StopIteration

iters = SimpleIterator(5)

# По итератору можно двигаться с помощью функции next()
print('Функция next:', next(iters))
print('Функция next:', next(iters))

for i in iters:
    print('Цикл for ... in: ', i)

# Вызовем исключение `StopIteration`
# т.к. итерация закончилась в цикле for
next(iters)

# Функция next: 0
# Функция next: 1
# Цикл for ... in:  2
# Цикл for ... in:  3
# Цикл for ... in:  4
# Traceback (most recent call last):
#   File "/home/script/Simple-Iterator.py", line 26, in <module>
#     next(iters)
#   File "/home/script/Simple-Iterator.py", line 15, in __next__
#     raise StopIteration
# StopIteration

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