def ma(self): return 'a'
def mb(self): return 'b'
def mc(self): return 'c'
class AB:
a = ma
b = mb
class BC:
b = mb
c = mc
class ABC:
a = ma
b = mb
c = mc
Порядок разрешения методов
В случае, когда надклассы имеют одинаковые методы, использование того или иного метода определяется порядком разрешения методов(method resolution order). Для «новых» классов узнать этот порядок очень просто с помощью атрибута __mro__
:
>>> 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:
Читать дальше
Конец ознакомительного отрывка
Купить книгу