深入Python3 (Dive Into Python3)笔记6--闭合与生成器

一切内容都是摘抄,主要是便于回忆和鼓励自己不要间断,更详细内容请见原帖地址:

《深入 python3 》中文版

http://woodpecker.org.cn/diveintopython3/index.html

 

 

6.1. 深入

6.2. 我知道,让我们用正则表达式!

re.sub 替换 所有的 匹配项,而不仅仅是第一个匹配项。因此该正则表达式将 caps 转换为 oops,因为无论是 c 还是 a 均被转换为 o 。

中括号表示“匹配这些字符的其中之一”。因此 [sxz] 的意思是: “s、 x 或 z”,但只匹配其中之一。

作为方括号中的第一个字符, ^ 有特别的含义:非。[^abc] 的意思是:“ 除了 a、 b 或 c 之外的任何字符”。

re.sub('([^aeiou])y$', r'/1ies', 'vacancy')

顺便,我还想指出可以将该两条正则表达式合并起来(一条查找是否应用该规则,另一条实际应用规则),使其成为一条正则表达式。它看起来是下面这个样子:其中多数内容看起来应该很熟悉:使用了在 案例研究:分析电话号码 中用到的记忆分组。该分组用于保存字母 y 之前的字符。然后在替换字符串中,用到了新的语法: /1,它表示“嘿,记住的第一个分组呢?把它放到这里。”在此例中, 记住了 y 之前的 c ,在进行替换时,将用 c 替代 c,用 ies 替代 y 。(如果有超过一个的记忆分组,可以使用 /2 和 /3 等等。)

6.3. 函数列表

def match_default(noun):
    return True
 
def apply_default(noun):
    return noun + 's'
 
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:
   		if matches_rule(noun):
			return apply_rule(noun)

现在有了一个 rules 数据结构——一个函数对的序列,而不是一个函数(plural())实现多个条规则。

由于所有的规则被分割成单独的数据结构,新的 plural() 函数可以减少到几行代码。使用 for 循环,可以一次性从 rules 这个数据结构中取出匹配规则和应用规则这两样东西(一条匹配对应一条应用)。在 for 循环的第一次迭代过程中, matches_rule 将获取 match_sxz,而 apply_rule 将获取 apply_sxz。在第二次迭代中(假定可以进行到这一步), matches_rule 将会赋值为 match_h,而 apply_rule 将会赋值为 apply_h 。该函数确保最终能够返回某个值,因为终极匹配规则 (match_default) 只返回 True,意思是对应的应用规则 (apply_default) 将总是被应用。

该技术能够成功运作的原因是 Python 中一切都是对象,包括了函数。数据结构 rules 包含了函数——不是函数的名称,而是实际的函数对象。

6.4. 匹配模式列表

build_match_and_apply_functions() 函数用于动态创建其它函数。它接受 pattern、 search 和 replace 三个参数,并定义了 matches_rule() 函数,该函数通过传给 build_match_and_apply_functions() 函数的 pattern 及传递给所创建的 matchs_rules() 函数的 word 调用 re.search() 函数,哇。

在动态函数中使用外部参数值的技术称为 闭合【closures】。基本上,常量的创建工作都在创建应用函数过程中完成:它接受一个参数 (word),但实际操作还加上了另外两个值(search 和 replace),该两个值都在定义应用函数时进行设置。

6.5. 匹配模式文件

全局的 open() 函数打开文件并返回一个文件对象。此例中,将要打开的文件包含了名词复数形式的模式字符串。with 语句创建了叫做 context【上下文】的东西:当 with 块结束时,Python 将自动关闭文件,即便是在 with 块中引发了例外也会这样。

split() 方法的第一个参数是 None,表示“对任何空白字符进行分隔(制表符或空白,没有区别)”。第二个参数是 3,意思是“针对空白分隔三次,丢弃该行剩下的部分。”像 [sxz]$ $ es 这样的行将被分割为列表 ['[sxz]$', '$', 'es'],意思是 pattern 获得值 '[sxz]$', search 获得值 '$',而 replace 获得值 'es'。

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

6.6. 生成器

make_counter 中出现的 yield 命令的意思是这不是一个普通的函数。它是一次生成一个值的特殊类型函数。可以将其视为可恢复函数。调用该函数将返回一个可用于生成连续 x 值的 生成器【Generator】。

next() 函数以一个生成器对象为参数,并返回其下一个值。对 counter 生成器第一次调用 next() ,它针对第一条 yield 语句执行 make_counter() 中的代码,然后返回所产生的值。在此情况下,该代码输出将为 2,因其仅通过调用 make_counter(2) 对生成器进行初始创建。

对同一生成器对象反复调用 next() 将确切地从上次调用的位置开始继续,直到下一条 yield 语句。所有的变量、局部数据等内容在 yield 时被保存,在 next() 时被恢复。下一行代码等待被执行以调用 print() 以打印出 incrementing x 。之后,执行语句 x = x + 1。然后它继续通过 while 再次循环,而它再次遇上的第一条语句是 yield x,该语句将保存所有一切状态,并返回当前 x 的值(当前为 3)。

“yield” 暂停一个函数。“next()” 从其暂停处恢复其运行。

6.6.1. 斐波那奇生成器

可以在 for 循环中直接使用像 fib() 这样的生成器。for 循环将会自动调用 next() 函数,从 fib() 生成器获取数值并赋值给 for 循环索引变量。(n)

这是一个很有用的用法:将一个生成器传递给 list() 函数,它将遍历整个生成器(就像前例中的 for 循环)并返回所有数值的列表。

6.6.2. 复数规则生成器

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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章