[Tutor] singleton pattern

Roeland Rengelink r.b.rigilink@chello.nl
Fri, 18 May 2001 17:06:02 +0200


alan.gauld@bt.com wrote:

Hi Alan, 

I snipped most of this, because I wanted to start over from here:

> .... but for a singleton we
> want to ensure that calls to the constructor return a pointer
> to *the same* instance.
> 

This is the essence of the Singleton pattern, We use it when we want to
ensure there's only one instance of a class. Let's call this class
Singleton

Now, Python (or C++ for that matter) doesn't return a pointer from the
constructor. The object reference (pointer) has allready been created by
the time we execute the constructor (the self in __init__(self)). So
there is absolutely
nothing we can do about the definition of class Singleton that will
magically
cause

>>> x = Singleton()
>>> y = Singleton()
>>> id(x) == id(y)
1

(Not without completely unpythonic namespace hackery)

So, there are two alternative approaches.

* the SingletonWrapper

class SingletonWrapper:
    singleton = None:
    def __init__(self):
	if SingletonWrapper.singleton is None:
            SingletonWrapper.singleton = Singleton()

    def __getattr__(self, attr):
        return getattr(SingletonWrapper.singleton, attr)
    def __setattr__(self, attr, value):
	setattr(SingletonWrapper.singleton, attr, value)
        
SingletonWrapper is not itself a singleton, but of Singleton there will
be only one instance, so:

>>> x = SingletonWrapper()
>>> y = SingletonWrapper()
>>> id(x) == id(y)
0
>>> x.__class__
<SingletonWrapper...>

i.e., x and y refer to different objects, but, x and y _behave_ as if
they are referring to the same object (the single Singleton instance),
So

>>> x.attr == y.attr

is always true. Arguably this is the important property of the singleton
pattern. 

Another way to implent this is in Singleton itself, by redirecting all
attribute
assignment to Singleton.__dict__.

class Singleton:
    ...
    def __setattr__(self, attr, val):
	self.__class__.__dict__[attr] == val        

This may be used as a Mixin to give a class singleton behaviour
The user may then have differen instance of Singleton that behave as
if they are all referring to the same object

* the SingletonFactory

class SingletonFactory:
    singleton = None
    def __call__(self):
        if SingletonFactory.singleton is None:
            SingletonFactory.singleton = Singleton()
        return SingletonFactory.singleton

SingletonFactory is also not itself a singleton, but of Singleton there
will again be only one instance, so:

>>> SF = SingletonFactory()
>>> x = SF()
>>> y = SF()
>>> id(x) == id(y)
1
>>> x.__class__ 
<Singleton ...>


There are endless variations on these two themes, of course.

One of the nice things you can do is store a reference to the Singleton
class
in either your SingletonWrapper or SingletonFactory, and then rename
SingletonWrapper or an instance of SingletonFactory to Singleton, so
when the user does x = Singleton(), she gets duped into believing that
she's calling the constructor of Singleton() directly, while she
actually calls the constructor of SingletonWrapper or __call__ of
SingletonFactory

The nice thing is that this prevents the user from constructing
Singleton() directly. However, I think this trickery may be the cause of
some of the confusion that has crept in this thread.

One point to take home from this post though, is that any attempt to
change SingletonWrapper or SingletonFactory themselves into singletons,
will lead rather soon into infinite regress.

Hope this helps,

Roeland
-- 
r.b.rigilink@chello.nl

"Half of what I say is nonsense. Unfortunately I don't know which half"