You are here: Home > Dive Into Python > Dynamic functions > plural.py, stage 5 | << >> | ||||
Dive Into PythonPython from novice to pro |
We've factored out all the duplicate code and added enough abstractions so that our pluralization rules are defined in a list of strings. The next logical step is to take these strings and put them in a separate file, where they can be maintained separately from the code that uses them.
First, let's create a text file that contains the rules we want. No fancy data structures, just space- (or tab-)delimited strings in three columns. We'll call it rules.en; “en” stands for English. These are the rules for pluralizing English nouns. We could add other rule files for other languages later.
Now let's see how we can use this rules file.
import re import string def buildRule((pattern, search, replace)): return lambda word: re.search(pattern, word) and re.sub(search, replace, word)def plural(noun, language='en'):
lines = file('rules.%s' % language).readlines()
patterns = map(string.split, lines)
rules = map(buildRule, patterns)
for rule in rules: result = rule(noun)
if result: return result
![]() |
We're still using the closures technique here (building a function dynamically that uses variables defined outside the function), but now we've combined the separate match and apply functions into one. (The reason for this change will become clear in the next section.) This will let us accomplish the same thing as having two functions, but we'll need to call it differently, as we'll see in a minute. |
![]() |
Our plural function now takes an optional second parameter, language, which defaults to en. |
![]() |
We use the language parameter to construct a filename, then open the file and read the contents into a list. If language is en, then we'll open the rules.en file, read the entire thing, break it up by carriage returns, and return a list. Each line of the file will be one element in the list. |
![]() |
As we saw, each line in our file really has three values, but they're separated by whitespace (tabs or spaces, it makes no difference). Mapping the string.split function onto this list will create a new list where each element is a tuple of three strings. So a line like [sxz]$ $ es will be broken up into the tuple ('[sxz]$', '$', 'es'). This means that patterns will end up as a list of tuples, just like we hard-coded it in stage 4. |
![]() |
If patterns is a list of tuples, then rules will be a list of the functions created dynamically by each call to buildRule. Calling buildRule(('[sxz]$', '$', 'es')) returns a function that takes a single parameter, word. When this returned function is called, it will execute re.search('[sxz]$', word) and re.sub('$', 'es', word). |
![]() |
Because we're now building a combined match-and-apply function, we need to call it differently. Just call the function, and if it returns something, then that's the plural; if it returns nothing (None), then the rule didn't match and we need to try another rule. |
So the improvement here is that we've completely separated our pluralization rules into an external file. Not only can the file be maintained separately from the code, but we've set up a naming scheme where the same plural function can use different rule files, based on the language parameter.
<< plural.py, stage 4 |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
plural.py, stage 6 >> |