ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


Python Cookbook

Cooking with Python
Seven Tasty Recipes for Programmers

07/11/2002

by Robin Parma, Alex Martelli, Scott David Daniels, Ben Wolfson, Nick Perkins, Anurag Uniya, Tim Keating, Rael Dornfest and Jeremy Hylton, authors of the Python Cookbook

1. Simple Tests Using Exceptions

Usually Python is straightforward, but there are a few unexpected exceptions.
Credits: Robin Parma

Problem

You want to know if the contents of a string represent an integer, which is not quite the same thing as checking whether the string contains only digits.

Solution

# try/except is generally the best approach to such problems:

def IsInt(str):
	"""Is the given string an integer?"""
	try:int(str)
	except ValueError:return 0
	else:return 1

Discussion

Use exceptions to perform simple tests that are otherwise laborious. If you want to know if the contents of a string represent an integer, just try to convert it. That's what IsInt() does. The try/except mechanism catches the exception raised when the string cannot be converted to an int, turning it into a harmless return 0. The else clause, which runs only when no exception is raised in the try clause, gives a return 1 when the string is OK.

Don't be misled by the word "exception," or by what is considered good style in different programming languages. Relying on exceptions and try/except is a useful Pythonic idiom.

2. Constants in Python

Liberal Python lets you rebind any variable; yet, there is an instance where you can protect your variables.
Credits: Alex Martelli

Problem

You need to define module-level variables that client-code cannot accidentally rebind, such as "named constants."

Solution

# Needs Python 2.1 or better. Put in const.py...:

class _Constants:
	class ConstError(TypeError): pass
	def __setattr__(self, name, value):
		if self.__dict__.has_key(name):
			raise self.ConstError, "Can't rebind const(%s)"%name
		self.__dict__[name ] = value
	def __delattr__(self, name):
		if self.__dict__.has_key(name):
			raise self.ConstError, "Can't unbind	const(%s)"%name
		raise NameError, name
import sys
sys.modules[__name__] = _Constants()

# now any client-code can:
import const
# and bind an attribute ONCE:
const.magic =23
# but NOT re-bind it:
# const.magic =88 # would raise const.ConstError

Discussion

In Python, any variable can be re-bound at will. Modules don't let you define special methods, such as an instance's __setattr__, to stop attribute re-binding. An easy solution (in Python 2.1 and up) is to use an instance as "module."

Python 2.1 and up no longer forces entries in sys.modules to be module objects. You can install an instance object there and take advantage of its attribute-access special methods while still having client-code get at it with import somename. You might see this as a more Pythonic "Singleton"-ish idiom (but also see "Singleton? We don't need no stinkin' Singleton: the Borg non-pattern"). Note that this recipe ensures a constant binding for a given name, not an object's immutability, which is quite a different issue.

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.

5. Factorial with Lambda

This recipe is all in fun. It's a bit of cleverness we hesitate to show you, but can't resist: lambda, recursion, a ternary operator, all in one line sure to make any Programming 101 instructor's head spin.
Credits: Anurag Uniya

Problem

You want to write a recursive function, such as factorial, using lambda (you probably made a bet about whether it could be done!).

Solution

f = lambda n: n-1 +abs(n-1) and f(n-1)*n or 1

Discussion

This recipe implements the recursive definition of the factorial function as a lambda form. Since lambda forms must be expressions, this is slightly tricky. If/else, a statement, is not allowed inside an expression. Still, a short-circuiting form of a Python idiom for "conditional (ternary) operator" takes care of that (see "Simulating the ternary operator in Python" in the Python Cookbook for other ways to simulate the ternary operator, both short-circuiting and not).

The real issue, of course, is that lambda's forte (for what little it deserves being called a "forte") is making anonymous functions-how then do we recurse? This is what makes this recipe's subject a good bet to win a drink from your Python-using friends and acquaintances misguided enough not to have read the Python Cookbook from cover to cover. Make sure the terms of the bet only mention lambda and do not specify that the resulting function is to be left unnamed, of course! Some might consider this cheating, but we Pythmen like to think of ourselves as a bunch of pragmatists.

6. Rolling Dice

We aren't sure if we like this one for its ingenuity or hate it for its obscurity! It may be twisted, but if your for loop is only summing things up, consider reducing your recipe.
Credits: Tim Keating

Problem

You need to generate pseudo-random numbers simulating the roll of several dice, where the number of dice and number of sides on each die are parameters.

Solution

import random
def dice(num,sides):
	return reduce(lambda x,y,s=sides:x +random.randrange(s),
		range(num+1))+num

Discussion

This recipe presents a simple but subtle function to let you generate random numbers by simulating a dice roll. The number of dice and the number of sides to each die are the parameters of the function. In order to roll 4d6 (four 6-sided dice), you would call dice(4,6). Simulating a dice roll is a good way to generate a random number with an expected "binomial" profile. For example, 3d6 will generate a bell-shaped (but discrete) probability curve with an average of 10.5.

After trying a more "manual" approach (a for loop with an accumulator), I found that using reduce is generally faster. It's possible this implementation could be faster still, as I haven't profiled it very aggressively; it's fast enough for my purposes :).

7. Make An XML-RPC Method Call

Taking advantage of Web services doesn't get much easier than this. Think of it as a recipe for ordering out!
Credits: Rael Dornfest and Jeremy Hylton

Problem

You need to make a method call to an XML-RPC server.

Solution

#needs Python 2.2 or xmlrpclib
from xmlrpclib import Server
server = Server("http://www.oreillynet.com/meerkat/xml-rpc/server.php")
class MeerkatQuery:
	def __init__(self, search, num_items=5, descriptions=0):
		self.search = search
		self.num_items = num_items
		self.descriptions = descriptions
q = MeerkatQuery("[Pp ]ython")
print server.meerkat.getItems(q)

Discussion

XML-RPC is a simple and lightweight approach to distributed processing. xmlrpclib, which makes it easy to write XML-RPC clients in Python, is part of the core Python library since Python 2.2, but you can also get it for older releases of Python from www.pythonware.com/products/xmlrpc/.

This recipe uses O'Reilly's Meerkat service, intended for syndication of contents such as news and product announcements. Specifically, the recipe queries Meerkat for the five most recent items mentioning either "Python" or "python." If you try this out, be warned that, depending on the quality of your net connection, the time of day, and the level of traffic on the Internet, response times from Meerkat are very variable: if the script takes a long time to answer, it doesn't mean you did something wrong-it just means you have to be patient!

O'Reilly & Associates recently released Python Cookbook (July 2002) .


Return to Python DevCenter.

Copyright © 2009 O'Reilly Media, Inc.