ГЛАВНАЯ      ДОКУМЕНТАЦИЯ      СТАТЬИ      ПРОГРАММЫ      ССЫЛКИ      ФОРУМ      ДРУГОЕ   

Объекты-одиночки

Часто по тем или иным причинам необходимо обеспечить уникальность какого-либо объекта. В этой заметке приводятся различные варианты реализации "одиночек" на языке Python.

Введение

        Часто возникает необходимость в таком виде объектов, чтобы все запрошенные объекты этого вида были идентичны, имели одинаковое поведение и находились в одинаковом состоянии. Именно такие объекты мы и будем называть одиночками. Идентичность объектов можно рассматривать как с точки зрения оператора is (то есть, это должен быть один и тот же объект), так и с точки зрения оператора == (для равенства достаточно правильно определить метод сравнения — __eq__ или __cmp__). Каким же образом можно обеспечить все эти требования на языке Python?

Модули в языке Python являются одиночками!

        Действительно, каждый модуль инициализирует только один раз — когда вы его импортируете (не будем брать в расчет возможность перезагрузить модуль с помощью функции reload). Кроме того, механизм импорта модулей обеспечивает "одиночество" всех глобальных объектов модуля, атрибутов классов модуля и так далее. Однако не всегда удобно выделять несколько строк кода в отдельный модуль с одной этой целью, а инициализация объектов модуля произойдет, уже когда потребуется модуль, но еще не сам объект-одиночка из него. Поэтому использование механизма импорта модулей для обеспечения "одиночества" подойдет только в случае, если инициализация "одиночки" не требует значительных затрат.
        Преведем простой пример. Часто требуется объект, чтобы обозначить что-то особенное, например поведение по умолчанию. В большинстве случаев для этих целей вполне подходит значение None. Но как быть, если необходимо рассматривать None на равне с любыми другими значениями? Нужен уникальный объект, предназначенный только для данной задачи. Самый простой способ — создать экземпляр типа object:
default = object()  # уникальный объект

def func(arg=default):
    if arg is _default:
        # действия по умолчанию
    else:
        # обычная обработка аргумента, в том числе None
        Представление такого объекта не слишком удобно при отладке. Более подходящим будет пустой класс:
class default: pass
        Если вы хотите защититься от случайного использования не по назначению, можно запретить создание его экземпляров:
class default:
    def __init__(self):
        raise ValueError
        Ну и, наконец, чтобы получить действительно красивое представление "одиночки" или наделить его какой-то другой дополнительной функциональностью — придется создавать экземпляр специально написанного класса:
class _Default:
    def __repr__(self):
        return 'default'

default = _Default()
del _Default

Уникальный объект или разделяемое состояние?

        А так ли нам необходима уникальность объекта? Может вполне достаточно того, что вполне достаточно иметь одинаковое состояние и поведение? Тогда вам должен понравиться простой класс, предложенный Алексом Мартелли:
class Borg:
    __shared_state = {}
    def __init__(self):
        self.__dict__ = self.__shared_state
        При необходимости можно и равенство всех экземпляров класса Borg обеспечить, переопределив метод __eq__. Имея доступ к текущему состоянию, можно обеспечить выполнение инициализации только один раз. Однако, для классов нового типа со слотами такое решение работать не будет: экземпляры таких классов не используют __dict__ для хранения атрибутов. Конечно, новые классы также могут иметь разделяемое состояние. Более того, с помощью дескрипторов можно красиво обеспечить разделение только части состояния объектов, но класс уже не будет таким простым.

Классический подход

        Пожалуй самый распространенный способ гарантировать единственность создаваемого экземпляра — создавать его не напрямую, а через функцию или статический метод класса. Это классическая модель одиночки (Singleton), описанная "бандой четырех" (E. Gamma, R. Helm, R. Johnson, J. Vlissides, "Design Patterns, Elements of Reusable Object-Oriented Software"). Именно такой подход следует использрвать в тех случаях, когда создание и инциализация экземпляра требует значительных ресурсов, а результат не всегда востребован. Для единственного объекта функция-конструктор может выглядеть так:
class _Class:
    # ...

_instance = None

def createInstance():
    global _instance
    if _instance is None:
        _instance = _Class()
    return _instance
        Если же объект должен быть единственным для каждого набора параметров, то можно держать словарь инициализированных объектов.
        Однако в языке Python нет необходимости определять отдельную функцию конструктор или статический метод, достаточно переопределить метод и уникальность объектов будет поддерживаться незаметно для пользователей:
class Singleton(object):
    __instance = None
    def __new__(cls):
        if cls.__instance is None:
            cls.__instance = self = object.__new__(cls)
            # необходимая инициализация
        return cls.__instance
        Следует помнить, что код, помещенный в метод __init__ этого класса будет выполняться при каждой попытке создания экзепляра (в этом редко возникает необходимость), в то время как код внутри инструкции if в методе __new__ выполняется, только когда экземпляр действительно создается.
        Последний вариант можно модифицировать таким образом, чтобы созданный экземпляр "жил" только тогда, когда он где-то используется, то есть на него есть ссылки:
import weakref

class Singleton(object):
    __instance = staticmethod(lambda: None)
    def __new__(cls):
        self = cls.__instance()
        if self is None:
            self = object.__new__(cls)
            cls.__instance = weakref.ref(self)
            # необходимая инициализация
        return self

Источники:
         www.python.ru

автор: Д.С.Откидач   
ПОМОЩЬ САЙТУ :
sms.Є®ЇЁ«Є  *PythonUA*
Для чего Вы используете Python?
Admin( 46 )
Web( 61 )
GUI( 37 )
Embedding ( 16 )
Другое( 34 )
Какими продуктами Вы пользовались?
Zope( 15 )
Plone( 1 )
TG( 7 )
Django( 15 )
Twisted( 5 )
Другими( 10 )
ДРУЗЬЯ:
LUG.DN.UA
D-FENS.ORG.UA
SLAV0NIC.XSS.RU
CETUS.COM.UA
ENTDEV.ORG
[Python Powered]
Rambler's Top100
Copyright © 2006 python.com.ua