<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # Chapter 6 閉合 與 生成器 > " My spelling is Wobbly. It’s good spelling but it Wobbles, and the letters get in the wrong places. " > — Winnie-the-Pooh ## 深入 出于傳遞所有理解的原因,我一直對語言非常著迷。我指的不是編程語言。好吧,是編程語言,但同時也是自然語言。使用英語。英語是一種七拼八湊的語言,它從德語、法語、西班牙語和拉丁語(等等)語言中借用了大量詞匯。事實上,“借用”是不恰當的詞匯,“掠奪”更加符合。或者也許叫“同化“——就像博格人(譯注:根據維基百科資料,Borg 是《星際旅行》虛構宇宙中的一個種族,該譯法未經原作者映證)。是的,我喜歡這樣。 `我們就是博格人。你們的語言和詞源特性將會被添加到我們自己的當中。抵抗是徒勞的。` 在本章中,將開始學習復數名詞。以及返回其它函數的函數、高級正則表達式和生成器。但首先,讓我們聊聊如何生成復數名詞。(如果還沒有閱讀[《正則表達式》一章](regular-expressions.html),現在也許是個好時機讀一讀。本章將假定您理解了正則表達式的基礎,并迅速進入更高級的用法。) 如果在講英語的國家長大,或在正規的學校學習過英語,您可能對下面的基本規則很熟悉 : * 如果某個單詞以 S 、X 或 Z 結尾,添加 ES 。_Bass_ 變成 _basses_, _fax_ 變成 _faxes_,而 _waltz_ 變成 _waltzes_。 * 如果某個單詞以發音的 H 結尾,加 ES;如果以不發音的 H 結尾,只需加上 S 。什么是發音的 H ?指的是它和其它字母組合在一起發出能夠聽到的聲音。因此 _coach_ 變成 _coaches_ 而 _rash_ 變成 _rashes_,因為在說這兩個單詞的時候,能夠聽到 CH 和 SH 的發音。但是 _cheetah_ 變成 _cheetahs_,因為 H 不發音。 * 如果某個單詞以發 I 音的字母 Y 結尾,將 Y 改成 IES;如果 Y 與某個原因字母組合發其它音的話,只需加上 S 。因此 _vacancy_ 變成 _vacancies_,但 _day_ 變成 _days_ 。 * 如果所有這些規則都不適用,只需加上 S 并作最好的打算。 (我知道,還有許多例外情況。_Man_ 變成 _men_ 而 _woman_ 變成 _women_,但是 _human_ 變成 _humans_。_Mouse_ 變成 _mice_ ; _louse_ 變成 _lice_,但 _house_ 變成 _houses_。_Knife_ 變成 _knives_ ;_wife_ 變成 _wives_,但是 _lowlife_ 變成 _lowlifes_。而且甚至我還沒有開始提到那些原型和復數形式相同的單詞,就像 _sheep_、 _deer_ 和 _haiku_。) 其它語言,當然是完全不同的。 讓我們設計一個 Python 類庫用來自動進行英語名詞的復數形式轉換。我們將以這四條規則為起點,但要記住的不可避免地還要增加更多規則。 ## 我知道,讓我們用正則表達式! 因此,您正在看著單詞,至少是英語單詞,也就是說您正在看著字符的字符串。規則說你必須找到不同的字符組合,然后進行不同的處理。這聽起來是正則表達式的工作! [[下載 `plural1.py`](examples/plural1.py)] ``` import re def plural(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' ``` 1. 這是一條正則表達式,但它使用了在 [_《正則表達式》_](regular-expressions.html) 一章中沒有講過的語法。中括號表示“匹配這些字符的其中之一”。因此 `[sxz]` 的意思是: “`s`、 `x` 或 `z`”,但只匹配其中之一。對 `$` 應該很熟悉了,它匹配字符串的結尾。經過組合,該正則表達式將測試 `noun` 是否以 `s`、 `x` 或 `z` 結尾。 2. 該 `re.sub()` 函數執行基于正則表達式的字符串替換。 讓我們看看正則表達式替換的細節。 ``` >>> import re <_sre.SRE_Match object at 0x001C1FA8> 'Mork' 'rook' 'oops' ``` 1. 字符串 `Mark` 包含 `a`、 `b` 或 `c` 嗎?是的,它包含 `a` 。 2. 好了,現在查找 `a`、 `b` 或 `c`,并將其替換為 `o`。`Mark` 變成了 `Mork`。 3. 同一函數將 `rock` 轉換為 `rook` 。 4. 您可能會認為該函數會將 `caps` 轉換為 `oaps`,但實際上并是這樣。`re.sub` 替換 _所有的_ 匹配項,而不僅僅是第一個匹配項。因此該正則表達式將 `caps` 轉換為 `oops`,因為無論是 `c` 還是 `a` 均被轉換為 `o` 。 接下來,回到 `plural()` 函數…… ``` def plural(noun): if re.search('[sxz]$', noun): return re.sub('$', 'es', noun) return re.sub('y$', 'ies', noun) else: return noun + 's' ``` 1. 此處將字符串的結尾(通過 `$` 匹配)替換為字符串 `es` 。換句話來說,向字符串尾部添加一個 `es` 。可以通過字符串鏈接來完成同樣的變化,例如 `noun + 'es'`,但我對每條規則都選用正則表達式,其原因將在本章稍后更加清晰。 2. 仔細看看,這里出現了新的變化。作為方括號中的第一個字符, `^` 有特別的含義:非。`[^abc]` 的意思是:“ _除了_ `a`、 `b` 或 `c` 之外的任何字符”。因此 `[^aeioudgkprt]` 的意思是除了 `a`、 `e`、 `i`、 `o`、 `u`、 `d`、 `g`、 `k`、 `p`、`r` 或 `t` 之外的任何字符。然后該字符必須緊隨一個 `h`,其后是字符串的結尾。所匹配的是以 H 結尾且 H 發音的單詞。 3. 此處有同樣的模式:匹配以 Y 結尾的單詞,而 Y 之前的字符 _不是_ `a`、 `e`、 `i`、 `o` 或 `u`。所匹配的是以 Y 結尾,且 Y 發音聽起來像 I 的單詞。 讓我們看看“否定”正則表達式的更多細節。 ``` >>> import re <_sre.SRE_Match object at 0x001C1FA8> >>> >>> re.search('[^aeiou]y$', 'day') >>> >>> ``` 1. `vacancy` 匹配該正則表達式,因為它以 `cy` 結尾,且 `c` 并非 `a`、 `e`、 `i`、 `o` 或 `u`。 2. `boy` 不匹配,因為它以 `oy` 結尾,可以明確地說 `y` 之前的字符不能是 `o` 。`day` 不匹配,因為它以 `ay` 結尾。 3. `pita` 不匹配,因為它不以 `y` 結尾。 ``` 'vacancies' >>> re.sub('y$', 'ies', 'agency') 'agencies' 'vacancies' ``` 1. 該正則表達式將 `vacancy` 轉換為 `vacancies` ,將 `agency` 轉換為 `agencies`,這正是想要的結果。注意,它也會將 `boy` 轉換為 `boies`,但這永遠也不會在函數中發生,因為我們首先進行了 `re.search` 以找出永遠不應進行該 `re.sub` 操作的單詞。 2. 順便,我還想指出可以將該兩條正則表達式合并起來(一條查找是否應用該規則,另一條實際應用規則),使其成為一條正則表達式。它看起來是下面這個樣子:其中多數內容看起來應該很熟悉:使用了在 [案例研究:分析電話號碼](regular-expressions.html#phonenumbers) 中用到的記憶分組。該分組用于保存字母 `y` 之前的字符。然后在替換字符串中,用到了新的語法: `\1`,它表示“嘿,記住的第一個分組呢?把它放到這里。”在此例中, 記住了 `y` 之前的 `c` ,在進行替換時,將用 `c` 替代 `c`,用 `ies` 替代 `y` 。(如果有超過一個的記憶分組,可以使用 `\2` 和 `\3` 等等。) 正則表達式替換功能非常強大,而 `\1` 語法則使之愈加強大。但是,將整個操作組合成一條正則表達式也更難閱讀,而且也沒有直接映射到剛才所描述的復數規則。剛才所闡述的規則,像 “如果單詞以 S 、X 或 Z 結尾,則添加 ES 。”如果查看該函數,有兩行代碼都在表述“如果以 S 、X 或 Z 結尾,那么添加 ES 。”它沒有之前那種模式更直接。 ## 函數列表 現在要增加一些抽象層次的內容。我們開始時定義了一系列規則:如果這樣,那樣做;否則前往下一條規則。現在讓我們對部分程序進行臨時的復雜化,以簡化另一部分。 ``` import re def match_sxz(noun): return re.search('[sxz]$', noun) def apply_sxz(noun): return re.sub('$', 'es', noun) def match_h(noun): return re.search('[^aeioudgkprt]h$', noun) def apply_h(noun): return re.sub('$', 'es', noun) return re.search('[^aeiou]y$', noun) return re.sub('y$', 'ies', noun) def match_default(noun): return True def apply_default(noun): return noun + 's' (match_h, apply_h), (match_y, apply_y), (match_default, apply_default) ) def plural(noun): if matches_rule(noun): return apply_rule(noun) ``` 1. 現在,每條匹配規則都有自己的函數,它們返回對 `re.search()` 函數調用結果。 2. 每條應用規則也都有自己的函數,它們調用 `re.sub()` 函數以應用恰當的復數變化規則。 3. 現在有了一個 `rules` 數據結構——一個函數對的序列,而不是一個函數(`plural()`)實現多個條規則。 4. 由于所有的規則被分割成單獨的數據結構,新的 `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`) 將總是被應用。 變量 “rules” 是一系列函數對。 該技術能夠成功運作的原因是 [Python 中一切都是對象](your-first-python-program.html#everythingisanobject),包括了函數。數據結構 `rules` 包含了函數——不是函數的名稱,而是實際的函數對象。在 `for` 循環中被賦值后,`matches_rule` 和 `apply_rule` 是可實際調用的函數。在第一次 `for` 循環的迭代過程中,這相當于調用 `matches_sxz(noun)`,如果返回一個匹配值,將調用 `apply_sxz(noun)` 。 如果這種附加抽象層令你迷惑,可以試著展開函數以了解其等價形式。整個 `for` 循環等價于下列代碼: ``` def plural(noun): if match_sxz(noun): return apply_sxz(noun) if match_h(noun): return apply_h(noun) if match_y(noun): return apply_y(noun) if match_default(noun): return apply_default(noun) ``` 這段代碼的好處是 `plural()` 函數被簡化了。它處理一系列其它地方定義的規則,并以通用的方式對它們進行迭代。 1. 獲取某匹配規則 2. 是否匹配?然后調用應用規則,并返回結果。 3. 不匹配?返回步驟 1 。 這些規則可在任何地方以任何方式定義。`plural()` 函數并不關心。 現在,新增的抽象層是否值得呢?嗯,還沒有。讓我們考慮下要向函數中新增一條規則時該如何操作。在第一例中,將需要新增一條 `if` 語句到 `plural()` 函數中。在第二例中,將需要新增兩個函數, `match_foo()` 和 `apply_foo()`,然后更新 `rules` 序列以指定新的匹配和應用函數按照其它規則按順序調用。 但是對于下一節來說,這只是一個跳板而已。讓我們繼續…… ## 匹配模式列表 其實并不是真的有必要為每個匹配和應用規則定義各自的命名函數。它們從未直接被調用,而只是被添加到 `rules` 序列并從該處被調用。此外,每個函數遵循兩種模式的其中之一。所有的匹配函數調用 `re.search()`,而所有的應用函數調用 `re.sub()`。讓我們將模式排除在考慮因素之外,使新規則定義更加簡單。 ``` import re def build_match_and_apply_functions(pattern, search, replace): return re.search(pattern, word) return re.sub(search, replace, word) ``` 1. `build_match_and_apply_functions()` 函數用于動態創建其它函數。它接受 `pattern`、 `search` 和 `replace` 三個參數,并定義了 `matches_rule()` 函數,該函數通過傳給 `build_match_and_apply_functions()` 函數的 `pattern` 及傳遞給所創建的 `matchs_rules()` 函數的 `word` 調用 `re.search()` 函數,哇。 2. 應用函數的創建工作采用了同樣的方式。應用函數只接受一個參數,并使用傳遞給 `build_match_and_apply_functions()` 函數的 `search` 和 `replace` 參數、以及傳遞給要創建 `apply_rule()` 函數的 `word` 調用 `re.sub()`。在動態函數中使用外部參數值的技術稱為 _閉合【closures】_。基本上,常量的創建工作都在創建應用函數過程中完成:它接受一個參數 (`word`),但實際操作還加上了另外兩個值(`search` 和 `replace`),該兩個值都在定義應用函數時進行設置。 3. 最后,`build_match_and_apply_functions()` 函數返回一個包含兩個值的元組:即剛才所創建的兩個函數。在這些函數中定義的常量( `match_rule()` 函數中的 `pattern` 函數,`apply_rule()` 函數中的 `search` 和 `replace` )與這些函數呆在一起,即便是在從 `build_match_and_apply_functions()` 中返回后也一樣。這真是非常酷的一件事情。 但如果此方式導致了難以置信的混亂(應該是這樣,它確實有點奇怪),在看看如何使用之后可能會清晰一些。 ``` ( ('[sxz]$', '$', 'es'), ('[^aeioudgkprt]h$', '$', 'es'), ('(qu|[^aeiou])y$', 'y$', 'ies'), ) for (pattern, search, replace) in patterns] ``` 1. 我們的復數形式“規則”現在被定義為 _字符串_ 的元組的元組(而不是函數)。每個組的第一個字符串是在 `re.search()` 中用于判斷該規則是否匹配的正則表達式。各組中的第二和第三個字符串是在 `re.sub()` 中將實際用于使用規則將名詞轉換為復數形式的搜索和替換表達式。 2. 此處的后備規則略有變化。在前例中,`match_default()` 函數僅返回 `True`,意思是如果更多的指定規則無一匹配,代碼將簡單地向給定詞匯的尾部添加一個 `s`。本例則進行了一些功能等同的操作。最后的正則表達式詢問單詞是否有一個結尾(`$` 匹配字符串的結尾)。當然,每個字符串都有一個結尾,甚至是空字符串也有,因此該規則將始終被匹配。因此,它實現了 `match_default()` 函數同樣的目的,始終返回 `True`:它確保了如果沒有更多的指定規則用于匹配,代碼將向給定單詞的尾部增加一個 `s` 。 3. 本行代碼非常神奇。它以 `patterns` 中的字符串序列為參數,并將其轉換為一個函數序列。怎么做到的?通過將字符串“映射”到 `build_match_and_apply_functions()` 函數。也就是說,它接受每組三重字符串為參數,并將該三個字符串作為實參調用 `build_match_and_apply_functions()` 函數。 `build_match_and_apply_functions()` 函數返回一個包含兩個函數的元組。也就是說該 `規則` 最后的結尾與前例在功能上是等價的:一個元組列表,每個元組都是一對函數。第一個函數是調用 `re.search()` 的匹配函數;而第二個函數調用 `re.sub()` 的應用函數。 此版本腳本的最前面是主入口點—— `plural()` 函數。 ``` def plural(noun): if matches_rule(noun): return apply_rule(noun) ``` 1. 由于 `規則` 列表與前例中的一樣(實際上確實相同),因此毫不奇怪 `plural()` 函數基本沒有發生變化。它是完全通用的,它以規則函數列表為參數,并按照順序調用它們。它并不關系規則是如何定義的。在前例中,它們被定義為各自命名的函數。現在它們通過將 `build_match_and_apply_functions()` 函數的輸出映射為源字符串的列表來動態創建。這沒有任何關系; `plural()` 函數將以同樣方式運作。 ## 匹配模式文件 目前,已經排除了重復代碼,增加了足夠的抽象性,因此復數形式規則可以字符串列表的形式進行定義。下一個邏輯步驟是將這些字符串放入一個單獨的文件中,因此可獨立于使用它們的代碼來進行維護。 首先,讓我們創建一份包含所需規則的文本文件。沒有花哨的數據結構,只有空白符分隔的三列字符串。將其命名為 `plural4-rules.txt`. ``` [sxz]$ $ es [^aeioudgkprt]h$ $ es [^aeiou]y$ y$ ies $ $ s ``` 下面看看如何使用該規則文件。 ``` import re 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 = [] pattern, search, replace)) ``` 1. `build_match_and_apply_functions()` 函數沒有發生變化。仍然使用了閉合技術:通過外部函數中定義的變量來動態創建兩個函數。 2. 全局的 `open()` 函數打開文件并返回一個文件對象。此例中,將要打開的文件包含了名詞復數形式的模式字符串。`with` 語句創建了叫做 _context【上下文】_的東西:當 `with` 塊結束時,Python 將自動關閉文件,即便是在 `with` 塊中引發了例外也會這樣。在 [《文件》](files.html) 一章中將學到關于 `with` 塊和文件對象的更多內容。 3. `for line in &lt;fileobject&gt;` 代碼從打開的文件中讀取數據,并將文本賦值給 `line` 變量。在 [《文件》](files.html) 一章中將學到更多關于讀取文件的內容。 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()` 函數所預期的匹配和應用函數列表。 此處的改進是將復數形式規則獨立地放到了一份外部文件中,因此可獨立于使用它的代碼單獨對規則進行維護。代碼是代碼,數據是數據,生活更美好。 ## 生成器 如果有個通用 `plural()` 函數解析規則文件不就更棒了嗎?獲取規則,檢查匹配,應用相應的轉換,進入下一條規則。這是 `plural()` 函數所必須完成的事,也是 `plural()` 函數必須做的事。 ``` def rules(rules_filename): with open(rules_filename, encoding='utf-8') as pattern_file: for line in pattern_file: pattern, search, replace = line.split(None, 3) yield build_match_and_apply_functions(pattern, search, replace) def plural(noun, rules_filename='plural5-rules.txt'): for matches_rule, apply_rule in rules(rules_filename): if matches_rule(noun): return apply_rule(noun) raise ValueError('no matching rule for {0}'.format(noun)) ``` _這段_代碼到底是如何運作的?讓我們先看一個交互式例子。 ``` >>> def make_counter(x): ... print('entering make_counter') ... while True: ... print('incrementing x') ... x = x + 1 ... <generator object at 0x001C9C10> entering make_counter 2 incrementing x 3 incrementing x 4 ``` 1. `make_counter` 中出現的 `yield` 命令的意思是這不是一個普通的函數。它是一次生成一個值的特殊類型函數。可以將其視為可恢復函數。調用該函數將返回一個可用于生成連續 `x` 值的 _生成器【Generator】_。 2. 為創建 `make_counter` 生成器的實例,僅需像調用其它函數那樣對它進行調用。注意該調用并不實際執行函數代碼。可以這么說,是因為 `make_counter()` 函數的第一行調用了 `print()`,但實際并未打印任何內容。 3. 該 `make_counter()` 函數返回了一個生成器對象。 4. `next()` 函數以一個生成器對象為參數,并返回其下一個值。對 `counter` 生成器第一次調用 `next()` ,它針對第一條 `yield` 語句執行 `make_counter()` 中的代碼,然后返回所產生的值。在此情況下,該代碼輸出將為 `2`,因其僅通過調用 `make_counter(2)` 對生成器進行初始創建。 5. 對同一生成器對象反復調用 `next()` 將確切地從上次調用的位置開始繼續,直到下一條 `yield` 語句。所有的變量、局部數據等內容在 `yield` 時被保存,在 `next()` 時被恢復。下一行代碼等待被執行以調用 `print()` 以打印出 `incrementing x` 。之后,執行語句 `x = x + 1`。然后它繼續通過 `while` 再次循環,而它再次遇上的第一條語句是 `yield x`,該語句將保存所有一切狀態,并返回當前 `x` 的值(當前為 `3`)。 6. 第二次調用 `next(counter)` 時,又進行了同樣的工作,但這次 `x` 為 `4`。 由于 `make_counter` 設置了一個無限循環,理論上可以永遠執行該過程,它將不斷遞增 `x` 并輸出數值。還是讓我們看一個更加實用的生成器用法。 ### 斐波那奇生成器 “yield” 暫停一個函數。“next()” 從其暫停處恢復其運行。 ``` def fib(max): while a < max: ``` 1. 斐波那契序列是一系列的數字,每個數字都是其前兩個數字之和。它從 0 和 `1` 開始,初始時上升緩慢,但越來越快。啟動該序列需要兩個變量:從 0 開始的 `a`,和從 `1` 開始的 `b` 。 2. `a` 是當前序列中的數字,因此對它進行 yield 操作。 3. `b` 是序列中下一個數字,因此將它賦值給 `a`,但同時計算下一個值 (`a + b`) 并將其賦值給 `b` 以供稍后使用。注意該步驟是并行發生的;如果 `a` 為 `3` 且 `b` 為 `5`,那么 `a, b = b, a + b` 將會把 `a` 設置 `5` (`b` 之前的值),將 `b` 設置為 `8` ( `a` 和 `b` 之前值的和)。 因此,現在有了一個連續輸出斐波那契數值的函數。當然,還可以使用遞歸來完成該功能,但這個方式更易于閱讀。同樣,它也與 `for` 循環合作良好。 ``` >>> from fibonacci import fib 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987] ``` 1. 可以在 `for` 循環中直接使用像 `fib()` 這樣的生成器。`for` 循環將會自動調用 `next()` 函數,從 `fib()` 生成器獲取數值并賦值給 `for` 循環索引變量。(`n`) 2. 每經過一次 `for` 循環, `n` 從 `fib()` 的 `yield` 語句獲取一個新值,所需做的僅僅是輸出它。一旦 `fib()` 的數字用盡(`a` 大于 `max`,即本例中的 `1000`), `for` 循環將會自動退出。 3. 這是一個很有用的用法:將一個生成器傳遞給 `list()` 函數,它將遍歷整個生成器(就像前例中的 `for` 循環)并返回所有數值的列表。 ### 復數規則生成器 讓我們回到 `plural5.py` 看看該版本的 `plural()` 函數是如何運作的。 ``` def rules(rules_filename): with open(rules_filename, encoding='utf-8') as pattern_file: for line in pattern_file: def plural(noun, rules_filename='plural5-rules.txt'): if matches_rule(noun): return apply_rule(noun) raise ValueError('no matching rule for {0}'.format(noun)) ``` 1. 此處沒有太神奇的代碼。由于規則文件中每行都靠包括以空白相間的三個值,因此使用 `line.split(None, 3)` 獲取三個“列”的值并將它們賦值給三個局部變量。 2. _然后使用了 yield。_ 但生產了什么呢?通過老朋友—— `build_match_and_apply_functions()` 動態創建的兩個函數,這與之前的例子是一樣的。換而言之, `rules()` 是_按照需求_連續生成匹配和應用函數的生成器。 3. 由于 `rules()` 是生成器,可直接在 `for` 循環中使用它。對 `for` 循環的第一次遍歷,可以調用 `rules()` 函數打開模式文件,讀取第一行,從該行的模式動態創建一個匹配函數和應用函數,然后生成動態創建的函數。對 `for` 循環的第二次遍歷,將會精確地回到 `rules()` 中上次離開的位置(在 `for line in pattern_file` 循環的中間)。要進行的第一項工作是讀取文件(仍處于打開狀態)的下一行,基于該行的模式動態創建另一匹配和應用函數,然后生成兩個函數。 通過第四步獲得了什么呢?啟動時間。在第四步中引入 `plural4` 模塊時,它讀取了整個模式文件,并創建了一份所有可能規則的列表,甚至在考慮調用 `plural()` 函數之前。有了生成器,可以輕松地處理所有工作:可以讀取規則,創建函數并試用它們,如果該規則可用甚至可以不讀取文件剩下的部分或創建更多的函數。 失去了什么?性能!每次調用 `plural()` 函數,`rules()` 生成器將從頭開始——這意味著重新打開模式文件,并從頭開始讀取,每次一行。 要是能夠兩全其美多好啊:最低的啟動成本(無需對 `import` 執行任何代碼),_同時_ 最佳的性能(無需一次次地創建同一函數)。哦,還需將規則保存在單獨的文件中(因為代碼和數據要涇渭分明),還有就是永遠不必兩次讀取同一行。 要實現該目標,必須建立自己的生成器。在進行_此工作_之前,必須對 Python 的類進行學習。 ## 深入閱讀 * [PEP 255: 簡單生成器](http://www.python.org/dev/peps/pep-0255/) * [理解 Python 的 “with” 語句](http://effbot.org/zone/python-with-statement.htm) * [Python 中的閉合](http://ynniv.com/blog/2007/08/closures-in-python.html) * [斐波那契數值](http://en.wikipedia.org/wiki/Fibonacci_number) * [英語的不規則復數名詞](http://www2.gsu.edu/~wwwesl/egw/crump.htm)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看