2010年7月13日 星期二

用Python也能輕鬆玩自然語言處理(2.3)

2.3 多講一點Python嘛:可以重複使用的程式碼

這個時候,你可能已經在Python的互動式直譯器裡重新輸入了很多的程式碼。如果你不小心弄錯了哪個部分時,你通常必須再次輸入一次那複雜的程式。雖然可以使用箭頭鍵來修改之前的命令,但這還是很沒效率。因此在本節中我們將學會利用兩種可以重新使用程式碼的方式:文字編輯器Python的函數


用文字編輯器產生程式碼


在Python互動式直譯器之下,你只需要敲入指令就可以馬上得到回應,但有時候你不希望這樣。一般來說在撰寫較多行的程式碼時就會仰賴文字編輯器,先打好程式碼後在一次拿給Python去執行。透過IDLE的介面,你可以從選項「File(檔案)」選單中建立一個新的視窗!試試看吧,先敲入一行程式「print 'Monty Python'」,然後存檔(把檔名取做「monty.py」),接著從「Run(執行)」選單中點選「Run Module(執行模組)」(別擔心!我們等等就會開始學習何謂模組了)。那麼最後,你就會看到IDLE視窗出現執行後的結果了:

  >>> ================================ RESTART ================================ 
>>> Monty Python
>>>

你可以試著敲入「from monty import *」,接著他就會再做同樣的事情一次!現在開始,你除了用互動式直譯器外,還可以利用文字編輯器來撰寫你的程式碼!使用直譯器可以很方便地去測試你的程式碼跑出來是不是你預想的結果,如果沒啥問題就可以把程式碼貼回文字編輯器裡頭(但別把提示號「>>>」也貼進去嘿!)然後就可以逐步擴大你的程式碼,寫好了在存起來,以後就可以一直重複使用了。記得檔名盡量短一點且有意義的,全部都使用小寫、字與字之間用底線隔開,副檔名為「.py」(像是「monty_python.py」)。

函數(Functions)


假設你的分析工作是一個字的各種型態,例如你的程式碼就必須能同時處理某個字以及它的複數型。又或者假設你某個工作必須做兩次,第一次處理一些文本,然後有新的資料進來又要處理一次...這些工作都是會導致你需要重複修改部分程式(而且感覺自己很笨)。


現在你可以試著學習更有效率且可靠的方法,就是自行撰寫函數!函數是由一段定義良好的程式碼區塊所組成(現在我們可以開始慢慢回答之前1.1的一些謎團XD),函數通常需要提領一些意欲處理的變數值(就是所謂的參數),相對地它也可能會在執行完一些動作後產生結果(也就是所謂的回傳值)。


我們利用關鍵字「def」來告訴Python我們準備要來定義函數,接著是函數名稱參數以及函數的真正內容。以下的函數是我們曾經在1.1看過(可以觀察一下import後面的描述,「division」就是一個可以被期待為執行除法的函數。)


>>> from __future__ import division
>>> def lexical_diversity(text):
...         return len(text) / len(set(text))


我們用「return」把這個函數的結果值回傳出來給我們!上面的例子裡這個函數其實也只做了一個回傳動作而已XD。下面這個例子也是相同的意思,只是改成了比較多行的程式碼去描述,我們把參數名稱改成「my_text_data」去告訴你可以換成你自己的data。


>>> def lexical_diversity(my_text_data): 
...     word_count = len(my_text_data) 
...     vocab_size = len(set(my_text_data)) 
...     diversity_score = word_count / vocab_size 
...     return diversity_score


注意我們在函數裡頭中所產生的新變數只是所謂的本地變數local variables)而不是函數外部那些可以隨時被存取的那種變數。所以現在我們已經定義了「lexical_diversity」函數,但是只有定義是沒用的,函數只有被呼叫called)時才會幫你做事情!


我們回到之前談到處理英文複數的問題,函數「plural()」會把一個單數名詞變成複數型態,當然!它還是會有出錯的時候XD(我們將會在4.4再詳細討論函數)


def plural(word): 
    if word.endswith('y'): return word[:-1] + 'ies'
    elif word[-1] in 'sx' or word[-2:] in ['sh', 'ch']: return word + 'es'
    elif word.endswith('an'): return word[:-2] + 'en'
    else: return word + 's'
  >>> plural('fairy'
'fairies'
>>> plural('woman'
'women'

可以發現「endswith()」函數必須搭配字串來使用!要呼叫這類型的函數,我們只需要提供對的物件名稱、時機與函數,就可以直接地隨時應用在程式碼裡面,而我們會把這些函數稱為「方法(methods」。


模組(Modules)


隨著時間的累積你一定會慢慢寫出一些有用的文字處理函數,也會慢慢發現你每次都要複製來貼上去的。到底你要的那組函數是躲在哪一的檔案裡?有的是不是我已經修改過了或還沒修改的?如果你可以事先把一些重要功能都定義在一個地方,當需要用到時,就直接存取它、就不用作一大堆複製程式碼的功夫了。


為了達到這個目標,我們把剛剛那個函數儲存到一個檔案(textproc.py)裡頭。現在你可以超簡單地去從檔案去匯入這些函數:


>>> from textproc import plural 
>>> plural('wish'
wishes
>>> plural('fan'
fen


很快地你就會發現這個函數有點問題,因為「fan」的複數型態明明就是「fans」才對。不過你不用特地去重新寫一個新的函數,你只要修改原本那個就可以了!所以在每段程式碼的結構中,用來處理複數問題的函數只會有一個,不用去擔心或怕弄錯。

像這樣一整組的變數與函數的結合,我們就會稱做Python的模組(module!而將一些相關功能的模組集合起來就稱為「套件(package」。像是NLTK工具包中處理布朗語料庫的一些功能就是一個模組的好例子,而那些可以處理不同語料庫的程式碼就是套件的例子!

不過要注意喔!如果你自己建立了一個Python的程式碼時,請不要把它的檔名取為「nltk.py」!因為當你載入時它會載到「真正的」NLTK工具包,要記得Python是會優先從它當下的資料夾目錄去做模組或套件程式碼的搜尋!

沒有留言: