Фрагмент кода Брюса Экеля из Design Pattern: я не понимаю, как это работает

Я читал " Размышления в питоне " Брюса Экеля. В настоящее время я читаю главу Pattern Pattern. В этой главе Eckel показывает различные реализации Singletons в Python. Но у меня есть неясное понимание кода Синглтона Алекса Мартелли (использующего наследование вместо частного вложенного класса).

Это мое понимание кода до сих пор:

  • Все объекты Singleton являются подклассами Borg
  • _shared_state изначально пустой словарь
  • _shared_state является глобальной переменной; Любые объекты, использующие Borg, будут иметь одинаковое значение _shared_state.

Мое замешательство до сих пор:

  • Какова цель этой линии: self.__dict__ = self._shared_state; или цель словаря
  • Как все объекты синглетонов в конечном итоге имеют одинаковое значение, хотя все они являются разными экземплярами класса.
  • В общем, я не знаю, как работает Борг

Большое Большое спасибо заранее!

-три

* Обновление: что хранится в _shared_state после каждого создания объекта Singleton?

#: Alex' Martelli's Singleton in Python
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´
output = '''
sausage
eggs
spam
spam
spam
<__main__.Singleton instance at 0079EF2C>
<__main__.Singleton instance at 0079E10C>
<__main__.Singleton instance at 00798F9C>
'''

4 ответа

Решение

Хорошие ответы пока, но позвольте мне также ответить прямо... self.__dict__ содержит атрибуты (т. е. состояние) экземпляра self (если его класс не делает особенных вещей, таких как определение __slots__;-). Таким образом, гарантируя, что все экземпляры имеют одинаковые __dict__ мы гарантируем, что все они имеют одинаковые атрибуты, то есть одно и то же состояние. Итак, Borg - это аккуратная реализация Python общего шаблона Monostate, но без каких-либо недостатков, которые Роберт Мартин определил в своем эссе о Monostate в C++.

См. Также эту ветку SO для Monostate vs Singleton - конечно, большая часть обсуждения НЕ относится к Python (очевидно, в Python Borg, напротив, не препятствует наследованию!, но тогда Singleton может быть столь же прозрачным благодаря например __new__ так что компромисс совсем другой...).

Самое смешное? За 8 с лишним лет, с тех пор как я впервые задумал о Борге (до того, как Дэвид Ашер, ныне генеральный директор Mozilla Messaging, предложил классное имя Борг), у меня была возможность использовать любой тип синглтона или моностата, возможно, четыре раза - и три раз из четырех я вскоре реорганизовал его в пользу более гибкого подхода!-) (в четвертый раз была подсистема, которая не оказалась очень успешной и не получила много последующей работы / обслуживания;-).

Во-вторых, самое смешное, что Гвидо лично ненавидит Борг;-). Не то, чтобы он тоже любил Синглтона: он думает, что в Python "синглтонарность", если необходимо, должна быть выполнена как модуль (или, я хотел бы добавить, экземпляр класса, маскирующийся под модуль - см., Например, мой наблюдение в разделе 7.2.6 Python в двух словах, например, в этом пиратском экземпляре;-). Но Борг, кажется, особенно оскорбляет его эстетику дизайна! -) Странно, потому что он выдвинул меня на членство в PSF, основываясь на моем эссе Five Easy Pieces, которое в основном ВСЕ о Борге (и его вариантах, и соображениях по этому поводу)...!-). Ах, хорошо, думаю, он может "ненавидеть грех, но любить грешника", хм?-)

class Borg:
  _shared_state = {}
  def __init__(self):
    self.__dict__ = self._shared_state

1) с Borg._shared_state инициализируется на уровне класса (не в __init__) оно эффективно статично, например, совместно используется всеми экземплярами класса.

2) self.__dict__ словарь, который есть у всех объектов; он содержит все атрибуты экземпляра. Таким образом,

self.a=1
assert(self.a == self.__dict__['a']) #True

3) Обратите внимание, что Borg означает "все экземпляры находятся в одном и том же состоянии", одиночный означает "только один экземпляр". Это в значительной степени тот же эффект. Алекс Мартелли указал, что Borg - это питоническое Monostate, поэтому смотрите SO на Monostate и Singleton.

Кажется, что его класс Singleton назван более удачно

class ThisClassHasSingletonBehavior(Borg):
    ....

так как

<__main__.Singleton instance at 0079EF2C>
<__main__.Singleton instance at 0079E10C>
<__main__.Singleton instance at 00798F9C>

доказывает, что x, y, z не являются тем же экземпляром, даже если они находятся в одном и том же состоянии. Так что это не ДЕЙСТВИТЕЛЬНО синглтон, он просто имеет тот же общий эффект и прост. AFAICT Borg - это элегантный шаблон python для поведения общего состояния (того же состояния), где Singleton - это элегантный шаблон cpp для того же самого. не то чтобы это поведение всегда было элегантным.

Сравнение с экземплярами обычного класса может помочь вам увидеть, что они делают:

>>> class NormalClass:
       def __init__(self, arg):
           self.x = arg


>>> a = NormalClass(1)
>>> b = NormalClass(2)
>>> a.x
1
>>> a.__dict__
{'x': 1}
>>> b.x
2
>>> b.__dict__
{'x': 2}

Note how the instances here each have their own unshared __dict__ where
their own x attribute is stored.

Редактирование для добавления: Теперь давайте сделаем так, чтобы эти обычные объекты использовали общий словарь и посмотрим, что произойдет...

>>> Another_Dictionary_We_Make = {}
>>> Another_Dictionary_We_Make['x'] = 1000
>>> Another_Dictionary_We_Make
{'x': 1000}
>>> a.__dict__ = Another_Dictionary_We_Make
>>> a.x
1000
>>> b.x
2
>>> b.__dict__ = Another_Dictionary_We_Make
>>> b.x
1000
>>> b.x = 777
>>> b.__dict__
{'x': 777}
>>> a.x
777
>>> a.__dict__
{'x': 777}

Это показывает, что делает dict одинаковым для обоих объектов после их создания. Borg/Singleton просто заставляют экземпляры использовать один и тот же словарь, инициализируя их для использования одного и того же слова, когда они создаются.

Я считаю, что вопрос был в следующем: как Borg._shared_state обновляться? Ответ: через self.__dict__ в каждом случае.self.__dict__ = self._shared_state дает вам доступ к Borg._shared_state через self.__dict__ каждого экземпляра. Когда вы меняете атрибут экземпляра, вы меняете его __dict__ и так как этот экземпляр __dict__ указывает на Borg._shared_state, вы на самом деле изменить Borg._shared_state, тоже. В питоне, когда вы говорите var1 = var2, var1 указывает на значение var2 и он имеет доступ к этому значению. Вот почему var1 = a_different_value на самом деле изменит значение var2 а также значение var1, Я также читаю вышеупомянутую книгу (онлайн через BitBucket). BitBucket говорит, что разработка остановилась несколько лет назад. Интересно, почему это так. Может быть, автор может прокомментировать это. Спасибо Алекс Мартелли за эту замечательную книгу.

Другие вопросы по тегам