I l@ve RuBoard |
5.22 Implementing the Singleton Design PatternCredit: Jürgen Hermann 5.22.1 ProblemYou want to make sure that only one instance of a class is ever created. 5.22.2 SolutionOne way to make a Singleton is to use a private inner class and delegate all operations to a single instance of that class: class Singleton: """ A Pythonic Singleton """ class _ _impl: """ Implementation of the Singleton class """ def spam(self): """ Just an example method that returns Singleton instance's ID """ return id(self) # The private class attribute holding the "one and only instance" _ _instance = _ _impl( ) def _ _getattr_ _(self, attr): return getattr(self._ _instance, attr) def _ _setattr_ _(self, attr, value): return setattr(self._ _instance, attr, value) 5.22.3 DiscussionThis recipe shows one way to implement the Singleton design pattern in Python (see Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley). A Singleton is a class that makes sure only one instance of it is ever created. Typically, such a class is used to manage resources that by their nature can exist only once. This recipe proposes an alternate approach to accessing such a single instance, which is arguably more Pythonic and more useful than the traditional implementation by a factory function. This recipe uses the Singleton._ _impl inner class as the class that is created only once. Note that inner classes are nothing special nor magical in Python, which is quite different from Java, and similar, instead, to C++. They are just classes that happen to have their class statement in the body of another class. The outer class, Singleton, ensures that exactly one instance of the inner class, Singleton._ _impl, is created on demand (i.e., the first time an instance of Singleton is created). Each instance of Singleton is a proxy to the one instance of Singleton._ _impl, using automatic delegation (see Recipe 5.9) to delegate to it all state and behavior. (Note that this idiom has also been called Letter/Envelope by other authors, such as Coplien; in that naming, Singleton would be the Envelope, and Singleton._ _impl the Letter.) While the id of each handle object is different, the id of the instance of the inner class that implements the Singleton behavior is constant. We can complete the module with the usual self-test idiom and show this id behavior: if _ _name_ _ == '_ _main_ _': s1 = Singleton( ) print id(s1), s1.spam( ) s2 = Singleton( ) print id(s2), s2.spam( ) When we run this module as a script, we get the following output; note that the second (inner) id is constant: 8172684 8176268 8168588 8176268 Of course, the inner class isn't really hidden, as with almost everything else in Python. If you need to protect against malicious attempts to access it, you need to use the rexec and Bastion standard modules and rely on a restricted execution sandbox (but this is really necessary only when you must run code that you do not trust, such as code you received from an unknown source). In addition to the secondary issue of using id for Singleton's instances, there is a concrete issue in terms of subclassability. It's not really feasible for client code to subclass the real class (Singleton._ _impl) in the recipe as presented. Subclassing the wrapper class (Singleton) is not the same thing, since other clients will still get a non-subclassed version. As the ability to subclass is high on the list of problems that the Singleton design pattern is supposed to resolve, this is a significant weakness. See Recipe 5.23 for a Pythonic solution to this problem. 5.22.4 See AlsoRecipe 5.9 and Recipe 5.23; Design Patterns: Elements of Reusable Object-Oriented Software, by E. Gamma, R. Helm, R. Johnson, and J. Vlissides (Addison-Wesley, 1995). |
I l@ve RuBoard |