Агрегация

Контейнеры

Под контейнером обычно понимают объект, основным назначением которого является хранение и обеспечение доступа к другим объектам. Контейнеры реализуют отношение «HAS–A» («ИМЕЕТ») между объектами. Встроенные типы, список и словарь — яркие примеры контейнеров. Можно построить собственные типы контейнеров, которые будут иметь свою логику доступа к хранимым объектам. В контейнере хранятся не сами объекты, а ссылки на них.

Для практических нужд в Python обычно хватает встроенных контейнеров (словаря и списка), но если это необходимо, можно создать и другие. Ниже приведен класс Стек, реализованный на базе списка:

class Stack:

 def __init__(self):

  """Инициализация стека"""

  self._stack = []

 def top(self):

  """Возвратить вершину стека (не снимая)"""

  return self._stack[-1]

 def pop(self):

  """Снять со стека элемент"""

  return self._stack.pop()

 def push(self, x):

  """Поместить элемент на стек"""

  self._stack.append(x)

 def __len__(self):

  """Количество элементов в стеке"""

  return len(self._stack)

 def __str__(self):

  """Представление в виде строки"""

  return " : ".join(["%s" % e for e in self._stack])

Использование:

>>> s = Stack()

>>> s.push(1)

>>> s.push(2)

>>> s.push("abc")

>>> print s.pop()

abc

>>> print len(s)

2

>>> print s

1 : 2

Таким образом, контейнеры позволяют управлять набором (любых) других объектов в соответствии со структурой их хранения, не вмешиваясь во внутренние дела объектов. Узнав интерфейс класса Stack, можно и не догадаться, что он реализован на основе списка, и каким именно образом он реализован с помощью него. Но для использования стека это не важно.

Примечание:

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

Итераторы

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

В следующем примере приведен итератор, выдающий значения из списка по принципу «считалочки» по N:

class Zahlreim:

 def __init__(self, lst, n):

  self.n = n

  self.lst = lst

  self.current = 0

 def __iter__(self):

  return self

 def next(self):

  if self.lst:

   self.current = (self.current + self.n — 1) % len(self.lst)

   return self.lst.pop(self.current)

  else:

   raise StopIteration

print range(1, 11)

for i in Zahlreim(range(1, 11), 5):

 print i,

Программа выдаст

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

5 10 6 2 9 8 1 4 7 3

В этой программе делегировано управление доступом к элементам списка (или любого другого контейнера, имеющего метод pop(n) для взятия и удаления n–го элемента) классу–итератору. Итератор должен иметь метод next() и возбуждать исключение StopIteration по завершении итераций. Кроме того, метод __iter__() должен выдавать итератор по экземпляру класса (в данном случае итератор — он сам (self)).

В настоящее время итераторы приобретают все большее значение, и о них много говорилось в лекции по функциональному программированию.

Более 800 000 книг и аудиокниг! 📚

Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением

ПОЛУЧИТЬ ПОДАРОК