闭合 与 生成器

来源:互联网 发布:js文本超出显示省略号 编辑:程序博客网 时间:2024/05/17 09:31

复数名词。以及返回其它函数的函数、高级正则表达式和生成器。

import redef plural(noun):              if re.search('[sxz]$', noun):             ①        return re.sub('$', 'es', noun)        ②    elif re.search('[^aeioudgkprt]h$', noun):        return re.sub('$', 'es', noun)           elif re.search('[^aeiou]y$', noun):              return re.sub('y$', 'ies', noun)         else:        return noun + 's'
  • re.search()是查询
  • 中括号表示“匹配这些字符的其中之一”。因此 [sxz] 的意思是: “s、 x 或 z”,但只匹配其中之一。
  • re.sub() 函数执行基于正则表达式的字符串替换。

否定”正则表达式

>>> import re>>> re.search('[^aeiou]y$', 'vacancy')  ①<_sre.SRE_Match object at 0x001C1FA8>>>> re.search('[^aeiou]y$', 'boy')      ②>>> >>> re.search('[^aeiou]y$', 'day')>>> >>> re.search('[^aeiou]y$', 'pita')     ③>>> 
  • vacancy 匹配该正则表达式,因为它以 cy 结尾,且 c 并非 a、 e、 i、 o 或 u。
  • boy 不匹配,因为它以 oy 结尾,可以明确地说 y 之前的字符不能是 o 。day 不匹配,因为它以 ay 结尾。

最终版本:
我还想指出可以将该两条正则表达式合并起来(一条查找是否应用该规则,另一条实际应用规则),使其成为一条正则表达式。用到的记忆分组。该分组用于保存字母 y 之前的字符。然后在替换字符串中,用到了新的语法: \1,它表示“嘿,记住的第一个分组呢?把它放到这里。”在此例中, 记住了 y 之前的 c ,在进行替换时,将用 c 替代 c,用 ies 替代 y 。(如果有超过一个的记忆分组,可以使用 \2 和 \3 等等。)

>>> re.sub('([^aeiou])y$', r'\1ies', 'vacancy')  ②'vacancies'

函数列表,闭合

import redef build_match_and_apply_functions(pattern, search, replace):def matches_rule(word):        return re.search(pattern, word)    def apply_rule(word):        return re.sub(search, replace, word)    return (matches_rule, apply_rule)rules = []with open('plural4-rules.txt', encoding='utf-8') as pattern_file:  ②    for line in pattern_file:                                      ③        pattern, search, replace = line.split(None, 3)             ④        rules.append(build_match_and_apply_functions(              ⑤                pattern, search, replace))
  1. build_match_and_apply_functions() 函数没有发生变化。仍然使用了闭合技术:通过外部函数中定义的变量来动态创建两个函数。
  2. 全局的 open() 函数打开文件并返回一个文件对象。此例中,将要打开的文件包含了名词复数形式的模式字符串。with 语句创建了叫做 context【上下文】的东西:当 with 块结束时,Python 将自动关闭文件,即便是在 with 块中引发了例外也会这样。在 《文件》 一章中将学到关于 with 块和文件对象的更多内容。
  3. for line in 代码从打开的文件中读取数据,并将文本赋值给 line 变量。在 《文件》 一章中将学到更多关于读取文件的内容。
  4. 文件中每行都有三个值,单它们通过空白分隔(制表符或空白,没有区别)。要将它们分开,可使用字符串方法 split() 。split() 方法的第一个参数是 None,表示“对任何空白字符进行分隔(制表符或空白,没有区别)”。第二个参数是 3,意思是“针对空白分隔三次,丢弃该行剩下的部分。”像 [sxz] es 这样的行将被分割为列表 [‘[sxz],’, ‘es’],意思是 pattern 获得值 ‘[sxz]search’,而 replace 获得值 ‘es’。对于短短的一行代码来说确实威力够大的。
  5. 最后,将 pattern 、 search 和 replace 传入 build_match_and_apply_functions() 函数,它将返回一个函数的元组。将该元组添加到 rules 列表,最终 rules 将储存 plural() 函数所预期的匹配和应用函数列表。

此处的改进是将复数形式规则独立地放到了一份外部文件中,因此可独立于使用它的代码单独对规则进行维护。代码是代码,数据是数据,生活更美好。

生成器(generator)

如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)

创建

  1. 要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator。

  2. 如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:
    yield 命令的意思是这不是一个普通的函数。它是一次生成一个值的特殊类型函数。可以将其视为可恢复函数。即等价于一个中断。
    最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

>>> def odd():...     print 'step 1'...     yield 1...     print 'step 2'...     yield 3...     print 'step 3'...     yield 5...

使用

generator的next()方法,所以,我们创建了一个generator后,基本上永远不会调用next()方法,而是通过for循环来迭代它。正确的方法是使用for循环。

>>> for n in fib(6):...     print n

优缺点

获得了什么呢?启动时间。在第四步中引入 plural4 模块时,它读取了整个模式文件,并创建了一份所有可能规则的列表,甚至在考虑调用 plural() 函数之前。有了生成器,可以轻松地处理所有工作:可以读取规则,创建函数并试用它们,如果该规则可用甚至可以不读取文件剩下的部分或创建更多的函数。

失去了什么?性能!每次调用 plural() 函数,rules() 生成器将从头开始——这意味着重新打开模式文件,并从头开始读取,每次一行。

要是能够两全其美多好啊:最低的启动成本(无需对 import 执行任何代码),同时 最佳的性能(无需一次次地创建同一函数)。哦,还需将规则保存在单独的文件中(因为代码和数据要泾渭分明),还有就是永远不必两次读取同一行

加强

在 python2.5 中,一些加强特性加入到生成器中,所以除了 next()来获得下个生成的值,用户可以将值回送给生成器[send()],在生成器中抛出异常,以及要求生成器退出[close()]

def gen(x):    count = xf = gen(5)print f.send(9)#发送数字9给生成器

参考:

http://old.sebug.net/paper/books/dive-into-python3/generators.html

0 0