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) 1

def plural(noun, language='en'):                             2
    lines = file('rules.%s' % language).readlines()          3
    patterns = map(string.split, lines)                      4
    rules = map(buildRule, patterns)                         5
    for rule in rules:                                      
        result = rule(noun)                                  6
        if result: return result                            
1 您在这里仍然使用闭包技术(动态构建一个使用在函数外部定义的变量的函数),但是现在您已经将单独的匹配和应用函数合并为一个函数。(这种变化的原因将在下一节中变得清晰。)这将使您能够完成与拥有两个函数相同的事情,但是您需要以不同的方式调用它,您稍后会看到。
2 我们的 plural 函数现在接受一个可选的第二个参数,language,默认为 en
3 您使用 language 参数构造文件名,然后打开文件并将内容读入列表。如果 languageen,那么您将打开 rules.en 文件,读取整个文件,按回车符将其分解,并返回一个列表。文件的每一行都将是列表中的一个元素。
4 正如您所见,文件中的每一行实际上都有三个值,但它们之间用空格(制表符或空格,没有区别)分隔。将 string.split 函数映射到此列表将创建一个新列表,其中每个元素都是一个由三个字符串组成的元组。因此,像 [sxz]$ $ es 这样的行将被分解为元组 ('[sxz]$', '$', 'es')。这意味着 patterns 最终将成为一个元组列表,就像您在阶段 4 中硬编码的那样。
5 如果 patterns 是一个元组列表,那么 rules 将是一个由每次调用 buildRule 时动态创建的函数组成的列表。调用 buildRule(('[sxz]$', '$', 'es')) 将返回一个接受单个参数 word 的函数。当调用此返回的函数时,它将执行 re.search('[sxz]$', word) 和 re.sub('$', 'es', word)
6 因为您现在正在构建一个组合的匹配和应用函数,所以您需要以不同的方式调用它。只需调用该函数,如果它返回了一些内容,那么这就是复数形式;如果它没有返回任何内容(None),则该规则不匹配,您需要尝试其他规则。

所以这里的改进是您已经将复数规则完全分离到一个外部文件中。该文件不仅可以与代码分开维护,而且您还设置了一个命名方案,其中相同的 plural 函数可以根据 language 参数使用不同的规则文件。

这里的缺点是,每次调用 plural 函数时,您都在读取该文件。我以为我可以不用“留给读者作为练习”这句话就写完这本书,但您还是看到了:为特定于语言的规则文件构建一个缓存机制,如果规则文件在调用之间发生变化,该机制会自动刷新自身留给读者作为练习。玩得开心。