Автор оригинала: Mark Sausville.
Итераторы и итераторы везде в Python. Мы обычно не знаем о итераторах, потому что синтаксис Python скрывает их от нас. Почти каждый раз, когда мы манипулируем типом последовательности (строки, списки, кортежи, множествами, массивами и т. Д.), Мы используем итератор за кулисами.
Итализатор представляет последовательность значений, каждый из которых возвращается по одному в то время, когда соответствующий итератор вызывается.
>>> import sys; sys.version '3.7.9 (default, Aug 31 2020, 17:10:11) [MSC v.1916 64 bit (AMD64)]'
В этой статье рассматриваются итераторский протокол для углубления понимания оснований и представляет некоторые из самых полезных инструментов в Itertools
Модуль, который может быть полезен, когда основы недостаточно, чтобы выполнить работу. Также мы рассмотрим, почему итераторы могут быть гораздо более эффективными, чем стандартные контейнеры.
Что такое итераторы и итераторы?
Список [1, 2, 3]
является неразмерным. Мы можем получить его элементы по одному, используя для-в построить.
l = list([1, 2, 3]) for i in l: print(i)
Выход:
1 2 3
Теперь давайте распределим, что происходит внутри. Во-первых, давайте посмотрим на методы, которые л Обеспечивает (функция dir Функция перечисляет методы объекта).
>>> dir(l) ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
Значительный метод наших текущих целей – __er____
Отказ Это то, что делает л вмешательны. __er____
Возвращает итератор Отказ Давайте получим наши руки на итераторе и исследуйте это.
>>> l.__iter__()>>> type(l.__iter__()) list_iterator
Еще один способ получить на итератор для итератора, это ИТЕР
функция. Как видите, это просто более сжатый способ получить итератор.
>>> my_iterator = iter(l); my_iterator>>> my_iterator = iter(l); my_iterator
Примечание : Здесь есть тонкость: каждый раз __er____
или ИТЕР
Вызывается, возвращается новый экземпляр итератор. Каждый может называться отдельно. Каждый из них независим, и работает с одним влиянием на других (ы). Это важно для параллелизма, когда несколько процессов необходимо самостоятельно работать на утечке. На данный момент мы можем отложить это в сторону и посмотреть на то, что мы можем сделать с объектом итератора.
>>> dir(my_iterator) ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']
Значительный метод наших текущих целей – __Next__
Отказ Это то, что делает объект итератором. __Next__
Метод возвращает следующее значение из потенциала при вызовах.
>>> my_iterator.__next__() 1 >>> my_iterator.__next__() 2
Функция встроена Далее ()
делает то же самое, что называет __Next__
Метод (похоже на ITER
и .__ ITER__
).
>>> next(my_iterator) 3
Теперь интересно посмотреть, что происходит, когда Следующий ()
называется снова. На данный момент мы достигли конца значений.
>>> next(my_iterator) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last)in ----> 1 next(my_iterator) StopIteration:
Как видите, итератор поднимает Заставка
Исключение (и будет продолжать делать это, если он снова называется). Это сигналы, которые не оставлены больше ценностей (мы говорим, что итератор исчерпан).
И теперь вы можете увидеть, что для-в
делает за кулисами. Фактический код делает что-то вроде следующего:
done = False it = iter(l) while not done: try: print(next(it)) except StopIteration: done = True
Выход:
1 2 3
Строительные итераторы
Теперь давайте построим наш собственный итератор, который делает что-то немного отличается, чтобы продемонстрировать, как построить свой собственный, а также посмотреть, как вышеупомянутые.
Это предпринимает намерение и размер шага, N (и дополнительный смещение) и вернет каждый N-й элемент.
class nth_elems(): def __init__(self, contents, stride, start=0): self.contents = contents self.stride = stride self.start = start self.pointer = self.start def __iter__(self): return self def __next__(self): if self.pointer < len(self.contents): value = self.contents[self.pointer] self.pointer += self.stride return value else: raise StopIteration thing = nth_elems(range(10), 3) print(thing) # <__main__.nth_elems at 0x2b0659e5088> print(type(thing)) # __main__.nth_elems print(dir(thing)) ''' ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'contents', 'pointer', 'start', 'stride'] ''' for t in thing: print(t) ''' 0 3 6 9 '''
Что тут происходит? Мы обертываем взаимосвязь ( диапазон (10)
в итераторе, который мы только что построили, и для цикла по уходу за управление нашим итератором (с Next ()
) и заботится о том, чтобы ловить запечатление, когда мы достиг конца).
Вы можете утверждать, что одно и то же можно было сделать с помощью LOOP, и вы будете правильными, но аргумент запуска добавляет функциональность, которая недоступна в цикле для цикла.
thing = nth_elems(range(10), 3, start=2) for t in thing: print(t) ''' 2 5 8 '''
Таким образом, итераторы могут быть использованы для создания пользовательских поведений, которые могут быть лучше подходящими к проблеме под рукой. А в обычной моде Python предоставляет модуль, который добавляет функциональность на базовый язык и позволяет использовать полезные шаблоны итерации без необходимости создавать их самостоятельно.
InterLude: почему итераторы
Итераторы и списки или кортежи Оба представлены коллекции элементов, которые могут быть доступны по одному за один раз и могут потребляться или обрабатываться для петлей и аналогичных конструкций. Зачем вообще использовать итераторы?
Причина проста: списки потребляют память для каждого элемента в списке. Итератор может получить или построить каждый элемент по мере необходимости и из-за того, что только требует достаточно памяти для хранения одного элемента.
Давайте посмотрим на пример, чтобы мы могли видеть именно то, что это может означать.
>>> import sys >>> sys.getsizeof(list(range(1000000))) 9000112 >>> sys.getsizeof(range(1000000)) 48
Поэтому, если вы получаете доступ к структуре данных одного элемента за раз, он может заплатить огромные дивиденды как в памяти, так и на производительности для реализации итератора для объекта.
Модуль iTertools
Модуль iTertools Это коллекция полезных шаблонов итераций и включает в себя 3 основных вида итераторов: бесконечные итераторы, конечные итераторы и комбинаторные итераторы. Мы приводим примеры каждого типа ниже.
Бесконечные итераторы
Бесконечные итераторы будут продолжать уступающие значения, пока не перестанете призывать их. Они отлично подходят для маркировки других повторных документов.
>>> from itertools import count >>> count() count(0) >>> list(zip('beluga', count())) [('b', 0), ('e', 1), ('l', 2), ('u', 3), ('g', 4), ('a', 5)]
>>> from itertools import cycle >>> list(zip('beluga', cycle([1, 2, 3]))) [('b', 1), ('e', 2), ('l', 3), ('u', 1), ('g', 2), ('a', 3)] >>> from itertools import repeat >>> list(zip('beluga', repeat([1, 2, 3]))) [('b', [1, 2, 3]), ('e', [1, 2, 3]), ('l', [1, 2, 3]), ('u', [1, 2, 3]), ('g', [1, 2, 3]), ('a', [1, 2, 3])]
Конечные итераторы
Конечные итераторы исчерпаны, когда их входы используются. Есть около десятка этих. Вот несколько примеров, чтобы соединить ваш аппетит:
Starmap.
У этого есть самое крутое имя. Требуется функция и снимательна и применяет функцию к элементам. Количество членов каждого элемента должно соответствовать количеству аргументов функции.
from math import sqrt from itertools import starmap discriminants = [x for x in starmap(lambda a, b, c: sqrt(b**2 - 4*a*c), [(1, -2 , 1), (1, 4, 4)])] print(discriminants) # [0.0, 0.0]
Цепь
Цепочка позволяет рассматривать несколько итераторов как одну последовательность.
from itertools import chain for c in chain('separate', 'words'): print(c) ''' s e p a r a t e w o r d s '''
Накапливаться
Накапливаются захватывает все промежуточные результаты нанесения функции двух аргументов последовательно к каждому элементу ввода илетнее и результат.
Это позволяет нам захватить проведение итогов. Вы можете использовать пользовательские функции, функции Lambda или операторы импорта для использования эффективных реализаций встроенных операторов Python с функциональным синтаксисом.
# factorial from itertools import accumulate import operator list(accumulate(range(1, 10), operator.mul)) # [1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
# operator.add is the default function # running total from itertools import accumulate list(accumulate(range(1, 10))) # [1, 3, 6, 10, 15, 21, 28, 36, 45]
Комбинаторные итераторы
Комбинаторные итераторы чрезвычайно удобны, когда вам нужно использовать группу комбинаций элементов.
>>> from itertools import product, permutations, combinations, combinations_with_replacement
Продукт
Продукт производит тот же результат, что и вложенное для цикла.
>>> list(product('abc', 'def')) [('a', 'd'), ('a', 'e'), ('a', 'f'), ('b', 'd'), ('b', 'e'), ('b', 'f'), ('c', 'd'), ('c', 'e'), ('c', 'f')]
Перестановки
Перестановки возвращает все возможные уникальные выборы длины N от ввода илетнее.
>>> list(permutations(['red', 'green', 'blue'], 2)) [('red', 'green'), ('red', 'blue'), ('green', 'red'), ('green', 'blue'), ('blue', 'red'), ('blue', 'green')]
Комбинации
Комбинации возвращает все возможные уникальные выборы длины N Из входного утерянного обращения игнорирования (то есть только один из [(«красный», зеленый), («зеленый», «красный»)]
).
>>> list(combinations(['red', 'green', 'blue'], 2)) [('red', 'green'), ('red', 'blue'), ('green', 'blue')]
Комбинации
Комбинации возвращают все возможные уникальные выборы длины n от входного поручения, игнорируясь, но позволяя нескольким вариантам одного и того же выбора.
>>> list(combinations_with_replacement(['red', 'green', 'blue'], 2)) [('red', 'red'), ('red', 'green'), ('red', 'blue'), ('green', 'green'), ('green', 'blue'), ('blue', 'blue')]
Закрытие замечаний
Документация для ITERTOOLS заканчивается группой рецептов, которые используют функции Itertools вместе со стандартным Python для получения широкого спектра шаблонов итерации. Столкнувшись к выбору итерации, это отличная идея проверить, есть ли применимость к проблеме под рукой.
Кроме того, есть другой модуль, more_itertools
Это реализует рецепты в документации Itertools и Многие Более полезные узоры. Мы заканчиваемся несколькими примерами, которые должны обеспечить мотивацию для изучения этого замечательного модуля.
>>> from more_itertools import flatten, pairwise, grouper
Сплющить
Флаттен удаляет один уровень вложенности из списка списков
>>> list(flatten([['a', 'b'], [1, 2]])) ['a', 'b', 1, 2]
Пары
Эта удобная функция возвращает все последовательные пары элементов.
>>> list(pairwise(['red', 'orange', 'green', 'blue'])) [('red', 'orange'), ('orange', 'green'), ('green', 'blue')]
Окуривать
Эта функция нарушает вход в куски Размер аргумент
>>> list(grouper(['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'], 3)) [('red', 'orange', 'yellow'), ('green', 'blue', 'indigo'), ('violet', None, None)]