Python DevCenter
oreilly.comSafari Books Online.Conferences.

advertisement


Cooking with Python: Seven Tasty Recipes for Programmers
Pages: 1, 2, 3

3. Singleton? We Don't Need No Stinkin' Singleton: The Borg Non-Pattern

New Python programmers perennially ask how to write Singleton in Python. Taking a page from Jon Bentley, Martelli offers us this Python Programming Pearl.
Credits: Alex Martelli



Problem

You thought you wanted to make sure that only one instance of a class was ever created, but then you realized you don't really care about the id() of instances, but rather about their state and behavior.

Solution

class Borg:

	__shared_state = {}
	def __init__(self):
		self.__dict__ = self.__shared_state
# and whatever else you want in your class-that's all!

Discussion

The Singleton design pattern (DP) has a catchy name, but the wrong focus: on identity rather than state. The Borg non-pattern has all instances share state instead, and Python makes it, literally, a snap.

Related Reading

Python Cookbook
By David Ascher, Matt Margolin, Alex Martelli

Note that __getattr__ and __setattr__ are not involved--they can be defined independently, for whatever other purposes you want, or left undefined (and __setattr__, if defined, is not called for the rebinding of __dict__ itself). This only works with "classic classes" (Python 2.1 and earlier, or classes not subclassing built-in types in 2.2 and later), since their instances keep all their per-instance state in self.__dict-2.2 classes with self.__slots__ do not support this idiom quite as smoothly (you can use getters/setters for such advanced, non-classic classes to deal with this issue, if you wish, but sticking to "classic classes" for these needs may be simplest).

The Singleton DP is all about ensuring that just one instance of a certain class is ever created. It has a catchy name (as well, admittedly, as attempting to deal with often-present forces), and is thus enormously popular. However, in my experience, it is most often not a good solution to those forces--it displays different sorts of problems in different object--models. What we should really want, typically, is to let as many instances be created as necessary, but all with shared state. Who cares about identity and behavior? It's state we usually care about! This alternative Pattern has also been called Monostate. Incidentally, I'd like to call Singleton "Highlander," since "there can be only one."

You can implement Monostate in many ways in Python, but the Borg design non-pattern is most often best. Simplicity is its greatest strength. Since the self.__dict__ of any instance (in Python 2.1 or earlier, or for classic classes in Python 2.2) can be re-bound, just re-bind it in __init__ to a class-attribute dictionary-that's all! Now, any reference or binding of an instance attribute will affect all instances equally-"we all are one," and all that jazz. David Ascher suggested the very appropriate name "Borg" for this non-pattern. It is a non-pattern, because it had no known uses at the time of publication. Two or more separate known uses are part of the prerequisites for being a Design Pattern!

Calling this recipe "a Singleton" is as silly as calling an arcade "an umbrella." Both may serve similar purposes (let you walk in the rain without getting wet)-solve similar forces, in DP parlance-but, as they do it in utterly different ways, they're not instances of the same pattern. If anything, as already mentioned, Borg has similarities to the Monostate alternative DP to Singleton (but Monostate is a DP, while Borg is not, and indeed a Python Monostate can perfectly well exist without being a Borg).

For reasons mysterious to me, people often conflate issues germane to Borg and Highlander with others that are orthogonal, 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's one instance of that object's class, or twenty-three, and whether those multiple instances share state, or not. A fruitful approach to problem solving is "divide and conquer"-make problems easier by "splitting" their aspects apart. Making problems harder by joining several aspects together must be an example of an approach called "conflate and suffer"!

4. Curry-Associating Parameters with a Function

What's a cookbook without a good curry recipe? This common functional programming idiom adds the spice of clarity to your programs.
Credits: Scott David Daniels, Ben Wolfson, Nick Perkins, and Alex Martelli

Problem

You need to wrap a function, or other callable, obtaining another callable with fewer formal arguments, keeping some of the original's arguments fixed to given values--such as when you need to curry a callable to make another.

Solution

class curry:

	def __init__(self, fun, *args, **kwargs):
		self.fun = fun
		self.pending = args[:]
		self.kwargs = kwargs.copy()
	def __call__(self, *args, **kwargs):
		if kwargs and self.kwargs:
			kw = self.kwargs.copy()
			kw.update(kwargs)
		else:
			kw = kwargs or self.kwargs
		return self.fun(*(self.pending + args), **kw)

Discussion

Currying is a way to bind some arguments with a function and wait for the rest of the arguments to show up later, named after the great mathematician Haskell Curry. You "curry in" the first few parameters to a function, giving you a function that takes subsequent parameters as arguments and then calls the original with all of those parameters. This recipe uses a class instance to hold the curried parameters until they're needed for use. For example:

double = curry(operator.mul,2)
triple = curry(operator.mul,3)

Currying is often implemented with lambda forms, but a dedicated class is clearer and more readable. A typical use of curry is to construct callback functions for GUI operations. When the operation does not really merit a new function name, curry can be useful in creating these little functions. This can be the case with commands for Tkinter buttons, for example:

self.button=Button(frame,text='A',
	command=curry(transcript.append,'A'))

Curry can also be used interactively to make versions of your functions with debugging-appropriate defaults, or initial parameters filled in for your current case. For example, database debugging work might begin by setting:

Connect = curry(ODBC.Connect, dsn='MyDataSet')
Another example of curry in debugging is wrapping methods:
def report(originalFunction, name, *args, **kw):
	print "%s(%s)"%(name, ','.join(map(repr,args) +
		[k+'='+repr(kw [k]) for k in kw.keys()] ))
	result = originalFunction(*args, **kw)
	if result is not None: print name, '==>', result
	return result
class Sink:
	def write(self, text): pass

dest = Sink()
dest.write = curry(report, dest.write, 'write')
print >>dest, 'this', 'is', 1, 'test'

If you are creating a function for regular use and there is a good choice for a name, the def fun(... form of function definition is often more readable and easily extended. Curry should be used when you feel the code is clearer with its use than without. Typically this will be to emphasize that you are only providing parameters to a "commonly used" (in this application) function, not providing separate processing.

You can curry the constructor of a class to give the illusion of a subclass:

BlueWindow =curry(Window,background="blue")

Of course, BlueWindow().__class__ is still Window , not a subclass, but if you're only changing default parameters, not behavior, currying is arguably more appropriate than subclassing anyway. You can still pass additional parameters to the curried constructor.

Pages: 1, 2, 3

Next Pagearrow





Sponsored by: