Автор оригинала: 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)]