Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] - نسخه متنی

David Ascher, Alex Martelli, Anna Ravenscroft

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید


Recipe 6.16. Avoiding the "Singleton" Design Pattern with the Borg Idiom


Credit: Alex Martelli, Alex A. Naanou


Problem


You want to make sure that only one
instance of a class is ever created: you don't care
about the id of the resulting instances, just
about their state and behavior, and you need to ensure
subclassability.


Solution


Application needs (forces) related to the
"Singleton" Design Pattern can be
met by allowing multiple instances to be created while ensuring that
all instances share state and behavior. This is more flexible than
fiddling with instance creation. Have your class inherit from the
following Borg class:

class Borg(object):
_shared_state = { }
def _ _new_ _(cls, *a, **k):
obj = object._ _new_ _(cls, *a, **k)
obj._ _dict_ _ = cls._shared_state
return obj

If you override _ _new_ _ in your class (very few
classes need to do that), just remember to use Borg._ _new_
_
, rather than object._ _new_ _, within
your override. If you want instances of your class to share state
among themselves, but not with instances of other subclasses of
Borg, make sure that your class has, at class scope,
the "state"ment:

    _shared_state = {  }

With this "data override", your
class doesn't inherit the
_shared_state attribute from Borg
but rather gets its own. It is to enable this "data
override" that
Borg's _ _new_ _
uses cls._shared_state instead of
Borg._shared_state.


Discussion



Borg in action

Here's a typical example of Borg
use:

if _ _name_ _ == '_ _main_ _':
class Example(Borg):
name = None
def _ _init_ _(self, name=None):
if name is not None: self.name = name
def _ _str_ _(self): return 'name->%s' % self.name
a = Example('Lara')
b = Example( ) # instantiating b shares self.name with a
print a, b
c = Example('John Malkovich')
# making c changes self.name of a & b too
print a, b, c
b.name = 'Seven' # setting b.name changes name of a & c too
print a, b, c

When running this module as a main script, the output is:

name->Lara name->Lara
name->John Malkovich name->John Malkovich name->John Malkovich
name->Seven name->Seven name->Seven

All instances of Example share state, so any
setting of the name attribute of any instance,
either in _ _init_ _ or directly, affects all
instances equally. However, note that the instance's
ids differ; therefore, since we have not defined
special methods _ _eq_ _ and _ _hash_
_
, each instance can work as a distinct key in a
dictionary. Thus, if we continue our sample code as follows:

    adict = {  }
j = 0
for i in a, b, c:
adict[i] = j
j = j + 1
for i in a, b, c:
print i, adict[i]

the output is:

name->Seven 0
name->Seven 1
name->Seven 2


If this behavior is not what you want, add
_ _eq_ _ and _ _hash_ _ methods
to the Example class or the Borg
superclass. Having these methods might better simulate the existence
of a single instance, depending on your exact needs. For example,
here's a version of Borg with these
special methods added:

class Borg(object):
_shared_state = { }
def _ _new_ _(cls, *a, **k):
obj = object._ _new_ _(cls, *a, **k)
obj._ _dict_ _ = cls._shared_state
return obj
def _ _hash_ _(self): return 9 # any arbitrary constant integer
def _ _eq_ _(self, other):
try: return self._ _dict_ _ is other._ _dict_ _
except AttributeError: return False

With this enriched version of Borg, the
example's output changes to:

name->Seven 2
name->Seven 2
name->Seven 2


Borg, Singleton, or neither?


The Singleton Design Pattern has a catchy
name, but unfortunately it also has the wrong focus for most
purposes: it focuses on object identity, rather than on object state
and behavior. The Borg design nonpattern makes all instances share
state instead, and Python makes implementing this idea a snap.

In most cases in which you might think of using Singleton or Borg,
you don't really need either of them. Just write a
Python module, with functions and module-global variables, instead of
defining a class, with methods and per-instance attributes. You need
to use a class only if you must be able to inherit from it, or if you
need to take advantage of the class' ability to
define special methods. (See Recipe 6.2 for a way to combine some of
the advantages of classes and modules.) Even when you do need a
class, it's usually unnecessary to include in the
class itself any code to enforce the idea that one
can't make multiple instances of it; other, simpler
idioms are generally preferable. For example:

class froober(object):
def _ _init_ _(self):
etc, etc
froober = froober( )

Now froober is by nature the only instance of its
own class, since name 'froober' has been rebound
to mean the instance, not the class. Of course, one might call
froober._ _class_ _( ), but it's
not sensible to spend much energy taking precautions against
deliberate abuse of your design intentions. Any obstacles you put in
the way of such abuse, somebody else can bypass. Taking precautions
against accidental misuse is way plenty. If the
very simple idiom shown in this latest snippet is sufficient for your
needs, use it, and forget about Singleton and Borg. Remember:
do the simplest thing that could possibly work.
On rare occasions, though, an idiom as simple as this one cannot
work, and then you do need more.

The Singleton Design Pattern (described previously in Recipe 6.15) is all about ensuring that
just one instance of a certain class is ever created. In my
experience, Singleton is generally not the best solution to the
problems it tries to solve, producing different kinds of issues in
various object models. We typically want to let as many instances be
created as necessary, but all with shared state. Who cares about
identity? It's state (and behavior) we care about.
The alternate pattern based on sharing state, in order to solve
roughly the same problems as Singleton does, has also been called
Monostate. Incidentally, I like to call
Singleton "Highlander" because
there can be only one.


In
Python, you can implement the Monostate Design Pattern in many ways,
but the Borg design nonpattern is often best. Simplicity is
Borg's greatest strength. Since the _
_dict_ _
of any instance can be rebound,
Borg in its _ _new_ _ rebinds the
_ _dict_ _ of each of its instances to a
class-attribute dictionary. Now, any reference or binding of an
instance attribute will affect all instances equally. I thank David
Ascher for suggesting the appropriate name Borg for
this nonpattern. Borg is a nonpattern because it had no known uses at
the time of its first publication (although several uses
are now known): two or more known uses are part
of the prerequisites for being a design pattern. See the detailed
discussion at http://www.aleax.it/5epl.

An excellent article by Robert Martin about Singleton and Monostate
can be found at http://www.objectmentor.com/resources/articles/SingletonAndMonostate.pdf.
Note that most of the disadvantages that Martin attributes to
Monostate are really due to the limitations of the languages that
Martin is considering, such as C++ and Java, and just disappear when
using Borg in Python. For example, Martin indicates, as
Monostate's first and main disadvantage, that
"A non-Monostate class cannot be converted into a
Monostate class through derivation"but that
is obviously not the case for Borg, which, through multiple
inheritance, makes such conversions trivial.


Borg odds and ends

The _
_getattr_ _
and _ _setattr_ _ special
methods are not involved in Borg's
operations. Therefore, you can define them independently in your
subclass, for whatever other purposes you may require, or you may
leave these special methods undefined. Either way is not a problem
because Python does not call _ _setattr_ _ in the
specific case of the rebinding of the instance's
_ _dict_ _ attribute.

Borg does not work well for classes that choose to
keep some or all of their per-instance state somewhere other than in
the instance's _ _dict_ _. So, in
subclasses of Borg, avoid defining _
_slots_ _
that's a memory-footprint
optimization that would make no sense, anyway, since
it's meant for classes that have a large number of
instances, and Borg subclasses will effectively have
just one instance! Moreover, instead of inheriting from built-in
types such as list or dict,
your Borg subclasses should use wrapping and automatic delegation, as
shown previously Recipe 6.5. (I named this latter twist
"DeleBorg," in my paper available
at http://www.aleax.it/5epl.)

Saying that Borg "is a Singleton"
would be as silly as saying that a portico is an umbrella. Both serve
similar purposes (letting you walk in the rain without getting
wet)solve similar forces, in design pattern parlancebut
since they do so in utterly different ways, they're
not instances of the same pattern. If anything, as already mentioned,
Borg has similarities to the Monostate alternative design pattern to
Singleton. However, Monostate is a design pattern, while Borg is not;
also, a Python Monostate could perfectly well exist without being a
Borg. We can say that Borg is an idiom that makes it easy and
effective to implement Monostate in Python.

For reasons mysterious to me, people often conflate issues germane to
Borg and Highlander with other, independent issues, such as access
control and, particularly, access from multiple threads. If you need
to control access to an object, that need is exactly the same whether
there is one instance of that object's class or
twenty of them, and whether or not those instances share state. A
fruitful approach to problem-solving is known as divide and
conquer
making problems easier to solve by splitting
apart their different aspects. Making problems more difficult to
solve by joining together several aspects must be an example of an
approach known as unite and suffer!


See Also


Recipe 6.5; Recipe 6.15; Alex Martelli,
"Five Easy Pieces: Simple Python
Non-Patterns" (http://www.aleax.it/5epl).

/ 394