Рубрики
Без рубрики

Итераторы, итералы и ITERTOOLS

https://youtu.be/yyliq2-hvdw Iterables и итераторы везде в Python. Мы обычно не знаем о итераторах, потому что синтаксис Python скрывает их от нас. Почти каждый раз, когда мы манипулируем типом последовательности (строки, списки, кортежи, множествами, массивами и т. Д.), Мы используем итератор за кулисами. Итализатор представляет последовательность значений, каждая из которых … итераторы, итераторы и ITERTOOLS Подробнее »

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