楼主: lastwinner

[参考文档] Python 研究(Dive Into Python)

[复制链接]
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
331#
 楼主| 发表于 2006-7-19 01:01 | 只看该作者
例 17.10. plural4.py 继续
patterns = \
  (
    ('[sxz]$', '$', 'es'),
    ('[^aeioudgkprt]h$', '$', 'es'),
    ('(qu|[^aeiou])y$', 'y$', 'ies'),
    ('$', '$', 's')
  )                                                
rules = map(buildMatchAndApplyFunctions, patterns)  
  我们的复数化规则现在被定义成一组字符串(不是函数)。 第一个字符串是你在调用 re.search 时使用的正则表达式;第二个和第三个字符串是你在通过调用 re.sub 来应用规则将名词变为复数时使用的搜索和替换表达式。  
  这很神奇。 把传进去的 patterns 字符串转换为传回来的函数。 如何做到的呢? 将这些字符串映射给 buildMatchAndApplyFunctions 函数之后,三个字符串参数转换成了两个函数组成的元组。 这意味着 rules 被转换成了前面范例中相同的内容:由许多调用 re.search 函数的匹配函数和调用 re.sub 的规则应用函数构成的函数组组成的一个元组。

我发誓这不是我信口雌黄:rules 被转换成了前面范例中相同的内容。 剖析 rules 的定义,你看到的是:

例 17.11. 剖析规则定义
rules = \
  (
    (
     lambda word: re.search('[sxz]$', word),
     lambda word: re.sub('$', 'es', word)
    ),
    (
     lambda word: re.search('[^aeioudgkprt]h$', word),
     lambda word: re.sub('$', 'es', word)
    ),
    (
     lambda word: re.search('[^aeiou]y$', word),
     lambda word: re.sub('y$', 'ies', word)
    ),
    (
     lambda word: re.search('$', word),
     lambda word: re.sub('$', 's', word)
    )
   )

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
332#
 楼主| 发表于 2006-7-19 01:01 | 只看该作者
例 17.12. plural4.py 的完成

def plural(noun):                                 
    for matchesRule, applyRule in rules:            
        if matchesRule(noun):                     
            return applyRule(noun)                 
  由于 rules 列表和前面的范例是相同的, plural 函数没有变化也就不另人诧异了。 记住,这没什么特别的,按照顺序调用一系列函数。 不必在意规则是如何定义的。 在 第 2 阶段,它们被定义为各具名称的函数。 在 第 3 阶段, 他们被定义为匿名的 lambda 函数。 现在第 4 阶段,它们通过 buildMatchAndApplyFunctions 映射原始的字符串列表被动态创建。 无所谓, plural 函数的工作方法没有变。

还不够兴奋吧!我必须承认,在定义 buildMatchAndApplyFunctions 时我跳过了一个微妙之处。 让我们回过头再看一下。

例 17.13. 回头看 buildMatchAndApplyFunctions

def buildMatchAndApplyFunctions((pattern, search, replace)):   
  注意到双括号了吗? 这个函数并不是真的接受三个参数,实际上只接受一个参数:一个三元素元组。但是在函数被调用时元组概念被展开了,元组的三个元素也被赋予了不同的变量: pattern, search 和 replace。 乱吗?让我们在使用中理解。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
333#
 楼主| 发表于 2006-7-19 01:02 | 只看该作者
例 17.14. 调用函数时展开元组
>>> def foo((a, b, c)):
...     print c
...     print b
...     print a
>>> parameters = ('apple', 'bear', 'catnap')
>>> foo(parameters)
catnap
bear
apple
  调用 foo 的正确方法是使用一个三元素元组。 函数被调用时,元素被分别赋予 foo 中的多个局部变量。

现在,让我们回过头看一看这个元组自动展开技巧的必要性。 patterns 是一个元组列表,并且每个元组都有三个元素。调用 map(buildMatchAndApplyFunctions, patterns),这并不 意味着是以三个参数调用 buildMatchAndApplyFunctions。 使用 map 映射一个列表到函数时,通常使用单参数:列表中的每个元素。 就 patterns 而言, 列表的每个元素都是一个元组,所以 buildMatchAndApplyFunctions 经常是以元组来调用,在 buildMatchAndApplyFunctions 中使用元组自动展开技巧将元素赋值给可以被使用的变量。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
334#
 楼主| 发表于 2006-7-19 01:02 | 只看该作者
17.6. plural.py, 第 5 阶段
你已经精炼了所有重复代码,也尽可能地把复数规则提炼到定义一个字符串列表。 接下来的步骤是把这些字符串提出来放在另外的文件中,从而可以和使用它们的代码分开来维护。

首先,让我们建立一个包含所有你需要的规则的文本文件。 没有什么特别的结构,不过是以空白(或者制表符)把字符串列成三列。 你把它命名为 rules.en, “en” 是英语的意思。 这些是英语名词复数的规则,你以后可以为其它语言添加规则文件。

例 17.15. rules.en
[sxz]$                  $               es
[^aeioudgkprt]h$        $               es
[^aeiou]y$              y$              ies
$                       $               s
现在来看看如何使用规则文件。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
335#
 楼主| 发表于 2006-7-19 01:02 | 只看该作者
例 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                           
  在这里你还将使用闭合技术(动态构建函数时使用函数外部定义的变量),但是现在你把原来分开的匹配函数和规则应用函数合二为一。 (你将在下一节中明了其原因) 你很快会看到,这与分别调用两个函数效果相同。
  咱们的 plural 函数现在接受的第二个参数是默认值为 en 的可选参数 language。
  你使用 language 参数命名一个文件,打开这个文件并读取其中的内容到一个列表。如果 language 是 en,那么你将打开 rules.en 文件,读取全部内容,以其中的回车符作为分割构建一个列表。 文件的每一行将成为列表的一个元素。
  如你所见,文件的每一行都有三个值,但是他们是以空白字符(制表符或者空格符,这没什么区别)分割。 用 string.split 函数映射列表来创建一个每个元素都是三元素元组的新列表。 因此,像 [sxz]$ $ es 这样的一行将被打碎并放入 ('[sxz]$', '$', 'es') 这样的元组。 这意味着 patterns 将最终变成元组列表的形式,就像 第 4 阶段 实打实编写的那样。
  如果 patterns 是一个元组列表,那么 rules 就可以通过一个个调用 buildRule 动态地生成函数列表。 调用 buildRule(('[sxz]$', '$', 'es')) 返回一个接受单参数 word 的函数。 当返回的函数被调用,则将执行 re.search('[sxz]$', word) 和 re.sub('$', 'es', word)。
  因为你现在构建的是一个匹配和规则应用合一的函数,你需要分别调用它们。 仅仅是调用函数,如果返回了内容,那么返回的便是复数;如果没有返回(也就是返回了None),那么该规则未能匹配,那么应该尝试其他规则。

这里的进步是你把复数规则完全分离到另外的文件中。不但这个文件可以独立于代码单独维护,而且你建立了一个命名规划使 plural 函数可以根据 language 参数使用不同的规则文件。

这里的缺陷是每次调用 plural 函数都需要去读取一次文件。 我想我可以在整本书中都不使用 “留给读者去练习”, 但是这里:为特定的语言规则文件建立一个缓存机制,并在调用期间规则文件改变时自动刷新 留给读者作为练习。 祝你顺利。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
336#
 楼主| 发表于 2006-7-19 01:03 | 只看该作者
17.7. plural.py, 第 6 阶段
现在你已准备好探讨生成器(Generator )了。

例 17.17. plural6.py
[php]
import re

def rules(language):                                                                 
    for line in file('rules.%s' % language):                                         
        pattern, search, replace = line.split()                                      
        yield lambda word: re.search(pattern, word) and re.sub(search, replace, word)

def plural(noun, language='en'):      
    for applyRule in rules(language):
        result = applyRule(noun)      
        if result: return result      
........
[/php]
这里使用了被称作生成器的技术,我不打算在你看过一个简单例子之前试图解释它。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
337#
 楼主| 发表于 2006-7-19 01:03 | 只看该作者
例 17.18. 介绍生成器
>>> def make_counter(x):
...     print 'entering make_counter'
...     while 1:
...         yield x               
...         print 'incrementing x'
...         x = x + 1
...     
>>> counter = make_counter(2)
>>> counter                  
<generator object at 0x001C9C10>
>>> counter.next()            
entering make_counter
2
>>> counter.next()            
incrementing x
3
>>> counter.next()            
incrementing x
4
  make_counter 中出现关键字 yield 意味着这不是一个普通的函数。它是一种每次生成一个值的特殊函数。 你可以把它看成是一个可恢复函数。调用它会返回一个生成器,它可以返回 x 的连续。  
  想要创建一个 make_counter 生成器的实例,只要象其它函数一样调用。 注意这并没有真正的执行函数代码。 你可以分辨出这一点,因为 make_counter 的第一行是 print 语句,然而没有任何内容输出。
  make_counter 函数返回一个生成器对象。
  你第一次调用生成器对象的 next() 方法,将执行 make_counter 中的代码执行到第一个 yield 语句,然后返回生产(yield)出来的值。 在本例中,这个值是 2,因为你是通过 make_counter(2) 来创建最初的生成器的。
  不断调用生成器对象的 next() 将从你上次离开的位置重新开始 并继续下去直到你又一次遇到 yield 语句。 接下来执行 print 语句来打印 incrementing x,然后执行 x = x + 1 语句来真正地增加。 然后你进入 while 的又一次循环,你所做的第一件事是 yield x。返回目前的 x 值(现在是3)。
  第二次你调用 counter.next() 时,你又做一遍相同的事情,但是这次 x 是 4。 如此继续。 因为 make_counter 设置的是一个无限循环,理论上你可以永远这样继续下去,不断地递增并弹出 x 值。 现在让我们看看生成器更具意义的应用。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
338#
 楼主| 发表于 2006-7-19 01:03 | 只看该作者
例 17.19. 使用生成器替代递归

def fibonacci(max):
    a, b = 0, 1      
    while a < max:
        yield a      
        a, b = b, a+b
  斐波纳契数列(Fibonacci sequence)是每个数都是前面两个数值和的一个数列。它从 0 和 1 开始,开始增长的很慢,但越来越快。 开始这个数列你需要两个变量: a 从 0开始, b 从 1 开始。
  a 是数列的当前值,弹出它。
  b 是数列的下一个数,把它赋值给 a,同时计算出(a+b)并赋值给 b 放在一边稍后使用。 注意这是并行发生的,如果 a 是 3, b 是 5,那么 a, b = b, a+b 将会设置 a 为 5 ( b 的原值), b 为 8 ( a 和 b 之和)。

这样你就有了成功生成 Fibonacci 数的函数了。 当然你也可以通过递归做到,但是这里的方法更加容易和理解。并且也与 for 工作的很好。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
339#
 楼主| 发表于 2006-7-19 01:03 | 只看该作者
例 17.20. for 循环中的生成器
>>> for n in fibonacci(1000):
...     print n,              
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
  你可以在 for 循环中直接使用 fibonacci 这样的生成器。 for 循环将会创建一个生成器对象并成功调用其 next() 方法获得值并赋予 for 循环变量(n)。
  每轮 for 循环 n 都从 fibonacci 的 yield 语句获得一个新的值。 当 fibonacci 超出数字限定(a 超过 max 你在这里限定的是 1000)很自然地退出 for 循环。

好了,让我们回到 plural 函数看看如何可以把它用起来。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
340#
 楼主| 发表于 2006-7-19 01:03 | 只看该作者
例 17.21. 生成器生成动态函数

def rules(language):                                                                 
    for line in file('rules.%s' % language):                                          
        pattern, search, replace = line.split()                                       
        yield lambda word: re.search(pattern, word) and re.sub(search, replace, word)

def plural(noun, language='en'):      
    for applyRule in rules(language):  
        result = applyRule(noun)      
        if result: return result      
  for line in file(...) 是从文件中一行行读取的通用方法,每次一行。 它能正常工作是因为 file 实际上返回一个生成器, 它的 next() 方法返回文件中的下一行。 简直太酷了,光是想想就让我满头大汗。
  这没有什么神奇之处。 还记得规则文件的每一行都用空白分开三个值吗?所以 line.split() 返回一个三元素元组,你把这些值赋给了 3 个局部变量。
  然后你不断地弹出。 你弹出什么呢? 一个使用 lambda 动态生成的函数,而这个函数实际上是一个闭合(把本地变量 pattern, search 和 replace 作为常量)。 换句话说, rules 是一个弹出规则函数的生成器。
  既然 rules 是一个生成器,你就可以在 for 循环中直接使用它。 for 循环的第一轮你调用 rules 函数,打开规则文件,读取第一行,动态构建一个根据规则文件第一行匹配并应用规则的函数。 for 循环的第二轮将会从上一轮 rules 中停下的位置( for line in file(...) 循环内部)读取规则文件的第二行,动态构建根据规则文件第二行匹配并应用规则的另一个函数。如此继续下去。

你在 第 5 阶段 得到的是什么? 第 5 阶段中,你读取整个规则文件并在使用第一条规则之前构建一个所有规则组成的列表。 现在有了生成器,你可以更舒适地做到这一切:你打开并读取第一条规则,根据它创建函数并使用之,如果它适用则根本不去读取规则文件剩下的内容,也不去建立另外的函数。

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表