ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 17.6. `plural.py`, 第 5 阶段 你已经精炼了所有重复代码,也尽可能地把复数规则提炼到定义一个字符串列表。接下来的步骤是把这些字符串提出来放在另外的文件中,从而可以和使用它们的代码分开来维护。 首先,让我们建立一个包含你需要的所有规则的文本文件。没有什么特别的结构,不过是以空格 (或者制表符) 把字符串列成三列。你把它命名为 `rules.en`,“en” 是英语的意思。这些是英语名词复数的规则,你以后可以为其它语言添加规则文件。 ## 例 17.15. `rules.en` ``` [sxz]$ $ es [^aeioudgkprt]h$ $ es [^aeiou]y$ y$ ies $ $ s ``` 现在来看看如何使用规则文件。 ## 例 17.16. `plural5.py` ``` 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 ``` | | | | --- | --- | | \[1\] | 在这里你还将使用闭合技术 (动态构建函数时使用函数外部定义的变量),但是现在你把原来分开的匹配函数和规则应用函数合二为一 (你将在下一节中明了其原因)。你很快会看到,这与分别调用两个函数效果相同,只是调用的方法稍有不同。 | | \[2\] | 咱们的 `plural` 函数现在接受的第二个参数是默认值为 `en` 的可选参数 `language`。 | | \[3\] | 你使用 `language` 参数命名一个文件,打开这个文件并读取其中的内容到一个列表。如果 `language` 是 `en`,那么你将打开 `rules.en` 文件,读取全部内容,以其中的回车符作为分隔构建一个列表。文件的每一行将成为列表的一个元素。 | | \[4\] | 如你所见,文件的每一行都有三个值,但是它们是以空白字符 (制表符或者空格符,这没什么区别) 分割。用 `string.split` 函数映射列表来创建一个每个元素都是三元素元组的新列表。因此,像 `[sxz]$ $ es` 这样的一行将被打碎并放入 `('[sxz]$', '$', 'es')` 这样的元组。这意味着 `patterns` 将最终变成元组列表的形式,就像[第 4 阶段](stage4.html "17.5. plural.py, 第 4 阶段")实打实编写的那样。 | | \[5\] | 如果 `patterns` 是一个元组列表,那么 `rules` 就可以通过一个个调用 `buildRule` 动态地生成函数列表。调用 `buildRule(('[sxz]$', '$', 'es'))` 返回一个接受单参数 `word` 的函数。当返回的函数被调用,则将执行 `re.search('[sxz]$', word) and re.sub('$', 'es', word)`。 | | \[6\] | 因为你现在构建的是一个匹配和规则应用合一的函数,你需要分别调用它们。仅仅是调用函数,如果返回了内容,那么返回的便是复数;如果没有返回 (也就是返回了`None`),那么该规则未能匹配,就应该尝试其他规则。 | 这里的进步是你把复数规则完全分离到另外的文件中。不但这个文件可以独立于代码单独维护,而且你建立了一个命名规划使 `plural` 函数可以根据 `language` 参数使用不同的规则文件。 这里的缺陷是每次调用 `plural` 函数都需要去读取一次文件。我想我可以在整本书中都不使用 “留给读者去练习”,但是这里:为特定的语言规则文件建立一个缓存机制,并在调用期间规则文件改变时自动刷新_留给读者作为练习_。祝你顺利。