[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 для циклических ссылок работает сборщик мусора.
Разумеется, при «чистой» агрегации циклических ссылок не возникает.
Например, при представлении дерева ссылки могут идти от родителей к детям и обратно от каждого дочернего узла к родительскому.
Для обеспечения ассоциаций объектов без свойственных ссылкам проблем с возможностью образования циклических ссылок, в Python для сложных структур данных и других видов использования, при которых ссылки не должны мешать удалению объекта, предлагается механизм слабых ссылок. Такая ссылка не учитывается при подсчете ссылок на объект, а значит, объект удаляется с исчезновением последней «сильной» ссылки.
Для работы со слабыми ссылками применяется модуль weakref
. Основные принципы его работы станут понятны из следующего примера:
>>> import weakref
>>>
>>> class MyClass(object):
... def __str__(self):
... return "MyClass"
...
>>>
>>> s = MyClass() # создается экземпляр класса
>>> print s
MyClass
>>> s1 = weakref.proxy(s) # создается прокси–объект
>>> print s1 # прокси–объект работает как исходный
MyClass
>>> ss = weakref.ref(s) # создается слабая ссылка на него
>>> print ss() # вызовом ссылки получается исходный объект
MyClass
>>> del s # удаляется единственная сильная ссылка на объект
>>> print ss() # теперь исходного объекта не существует
None
>>> print s1
Traceback (most recent call last):
File "
ReferenceError: weakly–referenced object no longer exists
К сожалению, поведение прокси–объекта не совсем такое, как у исходного: он не может быть ключом словаря, так как является нехэшируемым.
Иногда необходимо использовать метод, принадлежащий классу, а не его экземпляру. В этом случае можно описать статический метод. До появления декораторов (до Python 2.4) определять статический метод приходилось следующим образом:
class A(object):
def name():
return A.__name__
name = staticmethod(name)
print A.name()
a = A()
print a.name()
Статическому методу не передается параметр с экземпляром класса. Он ему попросту не нужен.
В Python 2.4 для применения описателей (descriptors) был придуман новый синтаксис — декораторы:
class A(object):
@staticmethod
def name():
return A.__name__
Смысл декоратора в том, что он «пропускает» определяемую функцию (или метод) через заданную в нем функцию. Теперь писать name три раза не потребовалось. Декораторов может быть несколько, и применяются они в обратном порядке.