2016-07-07 2 views
1

Я пытаюсь реализовать шаблон дизайна Borg, найденный здесь (воссозданный ниже): http://python-3-patterns-idioms-test.readthedocs.io/en/latest/Singleton.html.Python Borg design pattern, running метод создания класса

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 

Я хочу запустить определенный метод при первой инициализации этого класса, но никогда больше. Первоначально я попытался использовать некоторый логический флаг, но, по моему мнению, класс Singleton инициализируется несколько раз, но состояние и поведение являются общими для всех экземпляров. Поэтому любая инициализация, которую я выполняю в методе init, происходит более одного раза, поэтому флаги сбрасывались каждый раз, когда был инициализирован метод Singleton.

Я нашел решение, которое работает, но мне интересно, что это самый пифонический способ сделать это, потому что я не верю, что это так. Я сделал следующее:

class Singleton(Borg): 
def __init__(self, arg): 
    Borg.__init__(self) 
     if not self.__dict__: #I'm just checking that the namespace is empty, knowing it will be filled with something in the future. 
      firstInitializationMethod() 

Любая помощь очень ценится, и, пожалуйста, дайте мне знать, если вам нужна дополнительная информация. Я новичок в этом. Благодаря!

ответ

0

Я думаю, что ваше решение не так уж плохо с оговоркой в ​​том, что вы абсолютно необходимо заполнить что __dict__ до следующего init вызывается снова или firstInitializationMethod() будет называться более чем один раз. Это гарантировано в вашем примере из-за self.val = arg.

Однако, если ваш Simpleton не выполнил никаких назначений внутри пространства имен экземпляров класса в своем вызове __init__, ваше решение может выйти из строя.

Более простой и надежный способ, это просто использовать атрибут класса, как это:

class Singleton(Borg): 
    _first_initialization = True 
    def __init__(self,arg): 
     Borg.__init__(self) 
     if Singleton._first_initialization: 
      firstInitializationMethod() 
      Singleton._first_initialization = False 

Вы можете проверить этот код, просто заменив firstInitializationMethod() с печатью и создания некоторых Simpleton объектов, чтобы увидеть, что это будет только дозвонились один раз.

Это работает и _first_initialization не переписываются обратно True на каждом __init__ вызов, потому что класс пространство имена отдельный из класса экземпляра имен и Борг влияет только на последний (а именно делает все экземпляры Simpleton использовать те же __dict__).

Followup вопрос: Я попытался код с себя вместо Singleton и он все еще работал. Кажется, они решили одно и то же. Есть ли причина использовать Синглтон?

Рассмотрим этот код, используя эти два подхода, где SingletonSelfless является одним использованием Singleton._first_initialization, tinker() только возвращение self.__first_initialization:

a = Singleton('a') 
print(a) 
b = Singleton('b') 
print(a,b) 
c = Singleton('c') 
print(a,b,c) 
print(Singleton._first_initialization, a.tinker(),b.tinker(),c.tinker()) 

a = SingletonSelfless('a') 
print(a) 
b = SingletonSelfless('b') 
print(a,b) 
c = SingletonSelfless('c') 
print(a,b,c) 
print(SingletonSelfless._first_initialization, a.tinker(),b.tinker(),c.tinker()) 

и его вывод:

doing some init!! 
a 
b b 
c c c 
True False False False 
doing some init!! 
a 
b b 
c c c 
False False False False 

С практической точки зрения как работы, как мы хотели, еще есть четкая разница с _first_initialization значения переменных (переменных).

Ответ довольно прост. Несмотря на то, что пространство имен классов и пространство имен экземпляров являются отдельными, экземпляр по-прежнему может получить доступ к пространству имен классов. Но это происходит только в том случае, когда пространство имен экземпляров экземпляра fallback - имеет абсолютный приоритет, но когда он не может найти имя в своем собственном экземпляре namepsace, он пытается использовать первый класс. Итак, давайте посмотрим на __init__ в этом Singleton:

class Singleton(Borg): 
    _first_initialization = True 
    def __init__(self,arg): 
     Borg.__init__(self) 
     if self._first_initialization: 
      print('doing some init!!') 
      self._first_initialization = False 
     self.val = arg 
    def tinker(self): 
     return self._first_initialization 
    def __str__(self): return self.val 

Даже если экземпляр не имеет _first_initialization нашего if решается с помощью Singleton._first_initialization. Однако установка self._first_initialization на False создает переменную _first_initialization в пространстве имен экземпляров. Благодаря слишком Borg всем нашим экземпляры один и те же __dict__, так и на последующей инициализацию вызовов будет находиться в пространстве имен экземпляра класса (созданный при первом вызове __init__ со значением False) и нашим условным оператором будет решить, как мы хотим _first_initialization это - не делать еще firstInitializationMethod() (здесь распечатать для демонстрации пурпуры).

Однако наши оригинальные _first_initialization, находящиеся в пространстве имен классов, не изменились. Вот почему мы получаем True False False False.

В SingletonSelfless мы никогда не создаем _first_initialization экземпляров класса, поэтому вызов tinker() будет возвращаться к пространству имен классов. Вот почему есть 4 фальши - все вызовы указывают на один и тот же объект (SingletonSelfless._first_initialization bool variable).

В то время как в Singleton у нас есть два разных объекта - один из пространства имен классов, а другой в пространстве имен экземпляров класса, разделяемых между экземплярами. Так зачем использовать Singleton. вместо self.? Ну, для начинающих с первым мы «сбережем» невероятно крошечный бит памяти, имея там только один _first_initialization bool! Но реальная причина в том, что сложнее сменить переменную, скрывающуюся в пространстве имен классов. Если мы используем self._first_initialization и где-то в конце нашего кода что-то подобное произошло по какой-либо причине (или _shared_dict от Borg будет очищен или изменен затрагивающим, что бы жить там): a._first_initialization = 'Lol' или в Singleton или его дочерний метод self._first_initialization = 'ROFL' мы у вас возникнут серьезные проблемы, связанные с инициализацией новых объектов Singleton. С Singleton._first_initialization было бы хорошо, так как переменная, используемая для инициализации может быть изменена только путем явного Singleton._first_initialization='bad idea'

+0

Это частично не связано, но я попробовал код с 'self' вместо' Singleton', и он все еще работал. Кажется, они решили одно и то же. Есть ли причина использовать «Синглтон»? – vsocrates

+0

@ thePhilosopher Они различаются. Я обновлю свой ответ, чтобы показать вам разницу. –

+0

@ thePhilosopher Я обновил ответ со всей стеной текста относительно вашего последующего вопроса, но я надеюсь, что это достаточно читаемо, чтобы вы поняли. –