|I l@ve RuBoard|
3.20 Dispatching Based on Pattern Matches
Credit: Michael Robin
Once again, a class offers a good way to package together some state and some behavior:
import re class Dispatcher: def _dispatch(self, cmdList, str): """ Find a match for str in the cmdList and call the associated method with arguments that are the matching grouped subexpressions from the regex. """ for comment, pattern, command in cmdList: found = pattern.match(str) # or, use .search( ) if found: return command(self, *found.groups( )) def runCommand(self, cmd): self._dispatch(Commands, cmd) # example methods def cmd1(self, num, name): print "The number for %s is %d" % (name, int(num)) return 42 def cmd2(self, partnum): print "Widget serial #: %d" % int(partnum) Commands = [ [ 'Number-to-name correspondence', r'X (?P<num>\d),(?P<name>.*)$', Dispatcher.cmd1], [ 'Extract Widget part-number', r'Widget (?P<partnum>.*)$', Dispatcher.cmd2], ] # Prepare the Commands list for execution by compiling each re for cmd in Commands: try: cmd = re.compile( cmd ) except: print "Bad pattern for %s: %s" % ( cmd, cmd )
In Python, it's generally best to compile regular expressions into re objects. The re module does some caching of string-form regular expressions that you use directly, but it's still better to make sure that regular expressions are not needlessly recompiled. The string form is still available as r.pattern for any compiled re object r, anyway, should you need it (e.g., for debugging/logging purposes).
You can use regular expressions to match strings (or search into strings) and automatically call appropriate functions, passing as arguments substrings of the matched string that correspond to the groups of the regular expression.
This recipe exemplifies one approach to this solution. The idea is that:
r = self.runCommand("X 36,Mike")
cmd1(self, "36", "Mike")
and binds the variable r to 42, the result of cmd1.
This specific example might be best approached with direct string manipulation (testing str, then using the split method of strings), but regular expressions let you handle much more complicated cases with nearly equal ease.
An idiomatic Pythonic approach is to put each pattern to be compiled directly in the structure to be created at load-time. For example:
Cmds = ( (re.compile(r"^pa(t)t1$"), fn), ... )
This is simple, if you don't require any special processing, but I think it's a little prettier to avoid including code in data-structure initializers.
3.20.4 See Also
Documentation for the re module and regular-expression objects in the Library Reference.
|I l@ve RuBoard|