From 9f96f01e07ad3ab405d78c823e92d7c33c90efa0 Mon Sep 17 00:00:00 2001 From: Mark Pilgrim Date: Sat, 16 May 2009 01:38:26 -0400 Subject: [PATCH] use lists instead of tuples in generator chapter too [thanks A.H.] --- examples/plural2.py | 10 +++++----- examples/plural3.py | 2 +- examples/plural4.py | 2 +- examples/plural5.py | 2 +- examples/plural6.py | 6 +++--- generators.html | 20 ++++++++++---------- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/examples/plural2.py b/examples/plural2.py index 9c71d97..cae0e4a 100644 --- a/examples/plural2.py +++ b/examples/plural2.py @@ -31,11 +31,11 @@ def match_default(noun): def apply_default(noun): return noun + 's' -rules = ((match_sxz, apply_sxz), - (match_h, apply_h), - (match_y, apply_y), - (match_default, apply_default) - ) +rules = [[match_sxz, apply_sxz], + [match_h, apply_h], + [match_y, apply_y], + [match_default, apply_default] + ] def plural(noun): for matches_rule, apply_rule in rules: diff --git a/examples/plural3.py b/examples/plural3.py index d955793..ce407db 100644 --- a/examples/plural3.py +++ b/examples/plural3.py @@ -12,7 +12,7 @@ def build_match_and_apply_functions(pattern, search, replace): return re.search(pattern, word) def apply_rule(word): return re.sub(search, replace, word) - return (matches_rule, apply_rule) + return [matches_rule, apply_rule] patterns = \ [ diff --git a/examples/plural4.py b/examples/plural4.py index 89754a3..3b7bfaf 100644 --- a/examples/plural4.py +++ b/examples/plural4.py @@ -12,7 +12,7 @@ def build_match_and_apply_functions(pattern, search, replace): return re.search(pattern, word) def apply_rule(word): return re.sub(search, replace, word) - return (matches_rule, apply_rule) + return [matches_rule, apply_rule] rules = [] pattern_file = open('plural4-rules.txt') diff --git a/examples/plural5.py b/examples/plural5.py index c0df6a4..99c3321 100644 --- a/examples/plural5.py +++ b/examples/plural5.py @@ -12,7 +12,7 @@ def build_match_and_apply_functions(pattern, search, replace): return re.search(pattern, word) def apply_rule(word): return re.sub(search, replace, word) - return (matches_rule, apply_rule) + return [matches_rule, apply_rule] def rules(): for line in open('plural5-rules.txt'): diff --git a/examples/plural6.py b/examples/plural6.py index 91474d6..bb68394 100644 --- a/examples/plural6.py +++ b/examples/plural6.py @@ -12,13 +12,13 @@ def build_match_and_apply_functions(pattern, search, replace): return re.search(pattern, word) def apply_rule(word): return re.sub(search, replace, word) - return (matches_rule, apply_rule) + return [matches_rule, apply_rule] class LazyRules: - rules_f = 'plural6-rules.txt' + rules_filename = 'plural6-rules.txt' def __init__(self): - self.pattern_file = open(self.rules_f) + self.pattern_file = open(self.rules_filename) self.cache = [] def __iter__(self): diff --git a/generators.html b/generators.html index e4d94d3..bc303e0 100644 --- a/generators.html +++ b/generators.html @@ -145,11 +145,11 @@ def match_default(noun): def apply_default(noun): return noun + 's' -rules = ((match_sxz, apply_sxz), - (match_h, apply_h), - (match_y, apply_y), - (match_default, apply_default) - ) +rules = [[match_sxz, apply_sxz], + [match_h, apply_h], + [match_y, apply_y], + [match_default, apply_default] + ] def plural(noun): for matches_rule, apply_rule in rules: @@ -204,11 +204,11 @@ def build_match_and_apply_functions(pattern, search, replace): return re.search(pattern, word) def apply_rule(word): return re.sub(search, replace, word) - return (matches_rule, apply_rule) + return [matches_rule, apply_rule]
  1. build_match_and_apply_functions() is a function that builds other functions dynamically. It takes pattern, search and replace, then defines a matches_rule() function which calls re.search() with the pattern that was passed to the build_match_and_apply_functions() function, and the word that was passed to the matches_rule() function you’re building. Whoa.
  2. Building the apply function works the same way. The apply function is a function that takes one parameter, and calls re.sub() with the search and replace parameters that were passed to the build_match_and_apply_functions() function, and the word that was passed to the apply_rule() function you’re building. This technique of using the values of outside parameters within a dynamic function is called closures. You’re essentially defining constants within the apply function you’re building: it takes one parameter (word), but it then acts on that plus two other values (search and replace) which were set when you defined the apply function. -
  3. Finally, the build_match_and_apply_functions() function returns a tuple of two values: the two functions you just created. The constants you defined within those functions (pattern within matchFunction, and search and replace within applyFunction) stay with those functions, even after you return from build_match_and_apply_functions(). That’s insanely cool. +
  4. Finally, the build_match_and_apply_functions() function returns a list of two values: the two functions you just created. The constants you defined within those functions (pattern within matchFunction, and search and replace within applyFunction) stay with those functions, even after you return from build_match_and_apply_functions(). That’s insanely cool.

If this is incredibly confusing (and it should be, this is weird stuff), it may become clearer when you see how to use it. @@ -225,7 +225,7 @@ def build_match_and_apply_functions(pattern, search, replace): for (pattern, search, replace) in patterns]

  1. Our pluralization rules are now defined as a list of lists of strings (not functions). The first string in each group is the regular expression pattern that you would use in re.search() to see if this rule matches. The second and third strings in each group are the search and replace expressions you would use in re.sub() to actually apply the rule to turn a noun into its plural. -
  2. This line is magic. It takes the list of strings in patterns and turns them into a list of functions. How? By mapping the strings to the build_match_and_apply_functions() function, which just happens to take three strings as parameters and return a tuple of two functions. This means that rules ends up being exactly the same as the previous example: a list of tuples, where each tuple is a pair of functions, where the first function is the match function that calls re.search(), and the second function is the apply function that calls re.sub(). +
  3. This line is magic. It takes the list of strings in patterns and turns them into a list of functions. How? By mapping the strings to the build_match_and_apply_functions() function, which just happens to take three strings as parameters and return a list of two functions. This means that rules ends up being exactly the same as the previous example: a list of lists, where each (inner) list is a pair of functions. The first function is the match function that calls re.search(), and the second function is the apply function that calls re.sub().

Rounding out this version of the script is the main entry point, the plural() function. @@ -262,7 +262,7 @@ $ $ s return re.search(pattern, word) def apply_rule(word): return re.sub(search, replace, word) - return (matches_rule, apply_rule) + return [matches_rule, apply_rule] rules = [] pattern_file = open('plural4-rules.txt') @@ -277,7 +277,7 @@ finally:

  • The build_match_and_apply_functions() function has not changed. You’re still using closures to build two functions dynamically that use variables defined in the outer function.
  • Open the file that contains the pattern strings.
  • Read through the file one line at a time, using the for line in <fileobject> idiom. -
  • Each line in the file really has three values, but they’re separated by whitespace (tabs or spaces, it makes no difference). To split it out, use the split() string method. The first argument to the split() method is None, which means “split on any whitespace (tabs or spaces, it makes no difference).” The second argument is 3, which means “split on whitespace 3 times, then discard the rest of the line.” A line like [sxz]$ $ es will be broken up into the tuple ('[sxz]$', '$', 'es'), which means that pattern will get '[sxz]$', search will get '$', and replace will get 'es'. That’s a lot of power in one little line of code. +
  • Each line in the file really has three values, but they’re separated by whitespace (tabs or spaces, it makes no difference). To split it out, use the split() string method. The first argument to the split() method is None, which means “split on any whitespace (tabs or spaces, it makes no difference).” The second argument is 3, which means “split on whitespace 3 times, then discard the rest of the line.” A line like [sxz]$ $ es will be broken up into the list ['[sxz]$', '$', 'es'], which means that pattern will get '[sxz]$', search will get '$', and replace will get 'es'. That’s a lot of power in one little line of code.
  • Use a try..finally block to ensure the file object is closed.