MyTetra Share
Делитесь знаниями!
Итерируем все и вся
Время создания: 31.08.2017 21:01
Текстовые метки: knowledge
Раздел: Python - Iterators
Запись: xintrea/mytetra_db_mcold/master/base/15033155581a3k12lrcx/text.html на raw.githubusercontent.com

Итерируем все и вся


Насколько я успел понять по собственному опыту, при переходе на Python с другого языка программирования порой сложно привыкнуть к его специфическому подходу к циклам. Например, взять тот же самый for, который работает совершенно по-другому, нежели в других языках. Возьму на себя смелость рассказать о том, что мне самому поначалу было сложно осознать, а тем более использовать в своем коде — итераторы. Вещь на самом деле очень полезная, надо только уметь правильно ей пользоваться! ;)

АПД: Только сейчас заметил, что тема функционального программирования сегодня популярна как никогда :) Спасибо товарищу uj2 за раскрытие такой интересной темы, поддерживаю!

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

Итератор удобно использовать вместе с последовательностью. Любой объект, поддерживающий интерфейс итератора, имеет метод next(), позволяющий переходить на следующую ступень вычисления. Чтобы получить итератор по объекту (например, по списку), к нему нужно применить функцию iter(). Кстати, уже упомянутый цикл for тоже задействует итератор, только без дополнительных танцев — все делается автоматически. Для обработки сложных наборов данных можно подключить стандартный модуль itertools.

Создадим простейший итератор вручную:

testIt = iter([1, 2, 3, 4, 5]) 
print [x for x in testIt]


Конечно, это простейшее использование цикла 
for, только записанное немного по-другому.
«И что же тут нового?» — спросите вы. Да, в принципе, ничего, просто немного залезли глубже в структуру обработки последовательностей. Но теперь, как вам такое: функция 
iter() может принимать не только структуру данных, которая так запросто представляет свой итератор, но и два совсем других аргумента: функцию без аргументов и стоповое значение, на котором итерация остановится. Пример:

def getSimple(state=[]): 
  if len(state) < 4: 
    state.append(" ") 
    return " "

testIt2 = iter(getSimple, None) 
print [x for x in testIt2]


Пример основан на том, что в Python при отсутствии явного возвращения значения из функции возвращается значение 
None

Теперь рассмотрим несколько функций, работа которых основана на итераторах:

enumerate()
Предназначена для нумерации элементов структуры данных (в том числе и другого итератора). Возвращает список кортежей, в каждом из которых первый элемент — номер (начиная с нуля), а второй — элемент исходного итератора. Пример:

>>> print [x for x in enumerate("abcd")] 
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]


sorted()
Итератор, выполняющий сортировку:

>>> sorted('avdsdf') 
['a', 'd', 'd', 'f', 's', 'v']


itertools.chain()
Итератор, позволяющий объединить два разных итератора в один. Пример:

from itertools import chain 
it1 = iter([1,2,3]) 
it2 = iter([8,9,0]) 
for i in chain(it1, it2): 
  print i,


даст в результате «1 2 3 8 9 0».

itertools.count()
Итератор, выдающий бесконечные числа, начиная с заданного:

for i in itertools.count(1): 
  print i, 
  if i > 100: 
    break


Результат:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 
96 97 98 99 100 101

itertools.cycle()
Бесконечно повторяет заданную последовательность:

tango = [1, 2, 3] 
for i in itertools.cycle(tango): 
  print i,


Результат:
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1.. 

Так же в этом модуле есть аналоги 
map()starmap()filter() и zip(), называются они, естественно, itertools.imap()itertools.starmap()itertools.ifilter() и itertools.izip(). Их преимущество в том, что, в отличие от своих «обычных» аналогов тратят гораздо меньше памяти. Существует также пара фильтров: itertools.takewhile() и itertools.dropwhile():

for i in takewhile(lambda x: x > 0, [1, -2, 3, -3]): 
  print i, 
for i in dropwhile(lambda x: x > 0, [1, -2, 3, -3]): 
  print i,


Результат:

-2 3 -3

Дело в том, что 
takewhile() возвращает значения, пока условие истинно, а остальные значения даже не берет из итератора (именно не берет, а не высасывает все до конца!). И, наоборот, dropwhile() ничего не выдает, пока выполняется условие, зато потом выдает все без остатка.

Ну и, напоследок, давайте все-таки напишем свой собственный итератор (чуть было не дописал известную фразу Бендера)!

class Fibonacci: 
"""Итератор последовательности Фибоначчи до N""" 
  def __init__(self, N): 
    self.n, self.a, self.b, self.max = 0, 0, 1, N 
  def __iter__(self): 
    # сами себе итератор: в классе есть метод next() 
     return self 
  def next(self): 
     if self.n < self.max: 
       a, self.n, self.a, self.b = self.a, self.n+1, self.b, self.a+self.b 
       return a 
     else: 
       raise StopIteration 
# Использование: 
for i in Fibonacci(100): 
  print i,


Вот, собственно и вся основная теория. Если хотите узнать побольше тонкостей — читайте специальную литературу. Если есть какие-то замечания — буду рад выслушать :)

В статье использованы материалы лекции Романа Арвиевича Сузи «Элементы функционального программирования».

 
MyTetra Share v.0.59
Яндекс индекс цитирования