Листинг
>>> str.__mro__
(, , )
Это означает, что сначала методы ищутся в классе str, затем в basestring, а уже потом — в object.
Для «классических» классов порядок несколько отличается от порядка разрешения методов в «новых» классах. Нужно стараться избегать множественного наследования или применять его очень аккуратно.
Агрегация
Контейнеры
Под контейнером обычно понимают объект, основным назначением которого является хранение и обеспечение доступа к другим объектам. Контейнеры реализуют отношение «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)).
В настоящее время итераторы приобретают все большее значение, и о них много говорилось в лекции по функциональному программированию.
Ассоциация
Если в случае агрегации имеется довольно четкое отношение «ИМЕЕТ» (HAS–A) или «СОДЕРЖИТСЯ–В», которое даже отражено в синтаксисе Python:
Листинг
lst = [1, 2, 3]
if 1 in lst:
…
то в случае ассоциации ссылка на экземпляр другого класса используется без отношения включения одного в другой или принадлежности. О таком отношении между классами говорят как об отношении USE–A («ИСПОЛЬЗУЕТ»). Это достаточно общее отношение зависимости между классами.
В языке Python границы между агрегацией и ассоциацией несколько размыты, так как объекты при агрегации обычно не хранятся в области памяти, выделенной под контейнер (хранятся только ссылки).
Объекты могут также ссылаться друг на друга. В этом случае возникают циклические ссылки, которые при неаккуратном использовании могут привести (в старых версиях Python) к утечкам памяти. В новых версиях Python для циклических ссылок работает сборщик мусора.
Разумеется, при «чистой» агрегации циклических ссылок не возникает.
Например, при представлении дерева ссылки могут идти от родителей к детям и обратно от каждого дочернего узла к родительскому.
Читать дальше