Страницы

Поиск по вопросам

пятница, 20 декабря 2019 г.

singleton на python

#python #шаблоны_проектирования


Релизовал singleton с Wiki(ссылка на вики)

Мой код:

class SpriteManager(object):

    def __new__(cls):
        if not hasattr(cls, 'instance'):
             cls.instance = super(SpriteManager, cls).__new__(cls)
        return cls.instance

    def __init__(self):

        self.setSprite = set()


Каждый раз при вызове SpriteManager() вызывается конструктур.
Согласно концепции singleton, инициализация должна проходить один раз при первом вызове.
Как сделать,что бы конструктор вызывался один раз?
    


Ответы

Ответ 1



Могу предложить такой вариант: class SpriteManager(object): __instance = None @staticmethod def inst(): if SpriteManager.__instance == None: SpriteManager.__instance = SpriteManager() return SpriteManager.__instance #single call check def __init__(self): print("Constructor called!") a = SpriteManager.inst() b = SpriteManager.inst() print(a is b) По выводу видим, что конструктор вызывается только один раз: Constructor called! True Доступ к синглтону осуществляем через функцию inst(), например так: SpriteManager.inst().update()

Ответ 2



Патерны проектирования - это не кусок кода, это некоторая идея, которую можно реализовать различными способами. Тот же Singleton может быть реализован несколькими способами. Рассмотрим примеры. написано на основе http://python-3-patterns-idioms-test.readthedocs.org/en/latest/Singleton.html Одна из реализаций - делегировать "одиночку" внутреннему классу: class OnlyOne: class __OnlyOne: def __init__(self, arg): self.val = arg def __str__(self): return repr(self) + self.val instance = None def __init__(self, arg): if not OnlyOne.instance: OnlyOne.instance = OnlyOne.__OnlyOne(arg) else: OnlyOne.instance.val = arg def __getattr__(self, name): return getattr(self.instance, name) x = OnlyOne('sausage') print(x) y = OnlyOne('eggs') print(y) z = OnlyOne('spam') print(z) print(x) print(y) print(`x`) print(`y`) print(`z`) Имя внутреннего класса начинается с двойного подчеркивания, поэтому он является "частным" (private) и пользователь не сможет получить к нему прямой доступ. Внутренний класс содержит все те же методы, что и класс, который не был бы "одиночкой". Внешний класс при этом управляет созданием внутреннего класса через свой конструктор. Доступ к внутреннему классу осуществляется через делегирование при помощи метода __getattr__(), который пересылает все обращения к внешнему классу на внутренний класс. Из вывода программы можно увидеть, что хотя объекты и разные (т.е. создаются каждый раз), но внутренний класс создается только один раз и используется всеми созданными объектами. Вариация данной техники через использование классового метода __new__: class OnlyOne(object): class __OnlyOne: def __init__(self): self.val = None def __str__(self): return `self` + self.val instance = None def __new__(cls): # __new__ always a classmethod if not OnlyOne.instance: OnlyOne.instance = OnlyOne.__OnlyOne() return OnlyOne.instance def __getattr__(self, name): return getattr(self.instance, name) def __setattr__(self, name): return setattr(self.instance, name) x = OnlyOne() x.val = 'sausage' print(x) y = OnlyOne() y.val = 'eggs' print(y) z = OnlyOne() z.val = 'spam' print(z) print(x) print(y) Чаще всего в Singleton'е нас интересует только воможность делить одно состояние между объектами. Т.е. мы можем создать кучу различных объектов, но при этом они будут ссылаться на одно и то же состояние, поэтому изменение одного объекта (точнее его состояния) приведет к аналогичному изменению остальных объектов. Этого можно добиться, например, поместив в __dict__ каждого объекта ссылку на какое-то состояния: class Borg: _shared_state = {} def __init__(self): self.__dict__ = self._shared_state class Singleton(Borg): def __init__(self, arg): Borg.__init__(self) self.val = arg def __str__(self): return self.val x = Singleton('sausage') print(x) y = Singleton('eggs') print(y) z = Singleton('spam') print(z) print(x) print(y) print(`x`) print(`y`) print(`z`) Этот вариант имеет тот же эффект, что и в первом случае, но код получается более изящным. В первом случае вы должны явно реализовать поведение Singleton для каждого вашего класса, в то время как последний вариант спроектирован так, что его легко можно использовать в других классах. Более простая версия этого варианта использует тот факт, что существует только один экземпляр классовой переменной: class SingleTone(object): __instance = None def __new__(cls, val): if SingleTone.__instance is None: SingleTone.__instance = object.__new__(cls) SingleTone.__instance.val = val return SingleTone.__instance Два других способа определить Singleton: использование обертки и использование метаклассов. Первый подход может быть реализован через классовый декоратор: class SingletonDecorator: def __init__(self,klass): self.klass = klass self.instance = None def __call__(self,*args,**kwds): if self.instance == None: self.instance = self.klass(*args,**kwds) return self.instance @SingletonDecorator class foo: pass x=foo() y=foo() z=foo() x.val = 'sausage' y.val = 'eggs' z.val = 'spam' print(x.val) print(y.val) print(z.val) print(x is y is z) Второй подход использует метаклассы:: class SingletonMetaClass(type): def __init__(cls,name,bases,dict): super(SingletonMetaClass,cls)\ .__init__(name,bases,dict) original_new = cls.__new__ def my_new(cls,*args,**kwds): if cls.instance == None: cls.instance = \ original_new(cls,*args,**kwds) return cls.instance cls.instance = None cls.__new__ = staticmethod(my_new) class bar(object): __metaclass__ = SingletonMetaClass def __init__(self,val): self.val = val def __str__(self): return `self` + self.val x=bar('sausage') y=bar('eggs') z=bar('spam') print(x) print(y) print(z) print(x is y is z)

Ответ 3



Так как в методе __new__ у класса создаётся поле instance, достаточно просто получить его значение после хотя бы одной инициализации. a = SpriteManager() b = SpriteManager.instance print(a is b) # True

Ответ 4



Возможно вам понравится решение, предложенное в книге Бизли Python Cookbook David Beazley and Brian K. Jones где используется интересный метакласс, унаследовав который, вы получаете синглетон. Ваш __init__ будет вызываться один раз. class Singleton(type): def __init__(self, *args, **kwargs): self.__instance = None super().__init__(*args, **kwargs) def __call__(self, *args, **kwargs): if self.__instance is None: self.__instance = super().__call__(*args, **kwargs) return self.__instance else: return self.__instance # Example class Spam(metaclass=Singleton): def __init__(self): print('Creating Spam') a =Spam() b = Spam() print(a is b) Ваша задача - определить ваш инит в классе, наследующим Singleton. И - да! в вики по вашей ссылке уже другой пример :-) из PEP318: def singleton(cls): instances = {} def getinstance(): if cls not in instances: instances[cls] = cls() return instances[cls] return getinstance @singleton class MyClass: Как на меня эта реализация самая продуктивная.

Комментариев нет:

Отправить комментарий