The Singleton¶
Possibly the simplest design pattern is the singleton, which is a way to provide one and only one object of a particular type. To accomplish this, you must take control of object creation out of the hands of the programmer. One convenient way to do this is to delegate to a single instance of a private nested inner class:
# Singleton/SingletonPattern.py 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`) output = ''' sausage eggs spam spam spam '''
Because the inner class is named with a double underscore, it is private so the user cannot directly access it. The inner class contains all the methods that you would normally put in the class if it weren’t going to be a singleton, and then it is wrapped in the outer class which controls creation by using its constructor. The first time you create an OnlyOne, it initializes instance, but after that it just ignores you.
Access comes through delegation, using the __getattr__( ) method to redirect calls to the single instance. You can see from the output that even though it appears that multiple objects have been created, the same __OnlyOne object is used for both. The instances of OnlyOne are distinct but they all proxy to the same __OnlyOne object.
Note that the above approach doesn’t restrict you to creating only one object. This is also a technique to create a limited pool of objects. In that situation, however, you can be confronted with the problem of sharing objects in the pool. If this is an issue, you can create a solution involving a check-out and check- in of the shared objects.
A variation on this technique uses the class method __new__ added in Python 2.2:
# Singleton/NewSingleton.py 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) #
output = ''' sausage eggs spam spam spam '''
Alex Martelli makes the observation that what we really want with a Singleton is to have a single set of state data for all objects. That is, you could create as many objects as you want and as long as they all refer to the same state information then you achieve the effect of Singleton. He accomplishes this with what he calls the Borg [1], which is accomplished by setting all the __dict__s to the same static piece of storage:
# Singleton/BorgSingleton.py # Alex Martelli's 'Borg' 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 '''
This has an identical effect as SingletonPattern.py does, but it’s more elegant. In the former case, you must wire in Singleton behavior to each of your classes, but Borg is designed to be easily reused through inheritance.
A simpler version [2] of this takes advantage of the fact that there’s only one instance of a class variable:
# Singleton/ClassVariableSingleton.py 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
Two other interesting ways to define singleton [3] include wrapping a class and using metaclasses. The first approach could be thought of as a class decorator (decorators will be defined later in the book), because it takes the class of interest and adds functionality to it by wrapping it in another class:
# Singleton/SingletonDecorator.py 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 class foo: pass foo = SingletonDecorator(foo) 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)
The second approach uses metaclasses, a topic I do not yet understand but which looks very interesting and powerful indeed (note that Python 2.2 has improved/simplified the metaclass syntax, and so this example may change):
# Singleton/SingletonMetaClass.py 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)[[ Long, detailed, informative description of what metaclasses are and how they work, magically inserted here ]]
Exercises¶
- SingletonPattern.py always creates an object, even if it’s never used. Modify this program to use lazy initialization, so the singleton object is only created the first time that it is needed.
- Using SingletonPattern.py as a starting point, create a class that manages a fixed number of its own objects. Assume the objects are database connections and you only have a license to use a fixed quantity of these at any one time.
- Modify BorgSingleton.py so that it uses a class __new__( ) method.
[1] | From the television show Star Trek: The Next Generation. The Borg are a hive-mind collective: “we are all one.” |
[2] | From Dmitry Balabanov. |
[3] | Suggested by Chih-Chung Chang. |
© Copyright 2008, Creative Commons Attribution-Share Alike 3.0. Revision 59754c87cfb0 .
Versions latest Downloads pdf htmlzip epub On Read the Docs Project Home Builds Free document hosting provided by Read the Docs.