Python 入門指南 V2.00 - 單元 24 - 整合 Encrypt 類別




我們把編碼功能核心的 Encrypt 類別 (class) 放在 encrypt.py 中,而 GUI 的部份則是放在 encrypt_gui.py



完整程式請參考「範例程式碼」的 encrypt.pyencrypt_gui.py

為什麼要這麼做呢?新的 EncryptGUI 類別難道不能一樣放在 encrypt.py 裡嗎?或是直接擴充原本的 Encrypt 類別不就可以了嗎?


當然可以, EncryptGUI 類別放在 encrypt.py 裡或是擴充 Encrypt 類別都是沒關係的,可是這樣一來的話,如果我們需要在其他地方運用 Encrypt 類別編密碼的功能,就會發生大大的問題。


例如像是網頁介面或是手機應用程式,某些平台就算有 Python 但不一定能用 Tk ,因此這樣的設計導致程式碼重複利用性大大降低,反倒分開來後,核心歸核心,介面歸介面,每個模組 (module) 的設計都功能專一簡單化。


因此我們在 encrypt_gui.pyimport encrypt 就好嚕!另外也要 import tkinter


001from tkinter import *
002from tkinter.ttk import * 
003from encrypt import Encrypt

這裡也 import tkinter.ttk ,這是要套用 ttk 的 GUI 樣式。

我們發展 Encrypt 類別的時候採取檔案分版本的模式,從 encrypt01.py 一路到 encrypt07.py ,最終版本是重構的 encrypt.py ,而現在發展 EncryptGUI 只是略作改進,相關內容都是放在 encrypt_gui.py 中。

我們還要多設定三個實體屬性 (instance attribute) , self.e 用來儲存 Encrypt 物件 (object) , self.userinput 為儲存使用者輸入的文字, self.result 則是編碼結果


013self.e = None
014self.userinput = ""
015self.result = ""

self.userinputself.result 的初值都設定為空字串 (string) ,而 Encrypt 物件預計用 self.e 儲存,這裡同樣初值先設定為 None


這裡,我們預期使用者按下 New 按鈕就可以得到新的密碼表,因此相對應的 nm() 方法 (method) 就是用來新建 Encrypt 物件,實作如下


070# 按下 New 按鈕的事件
071def nm(self):
072   self.e = Encrypt()
073   self.dt["text"] = self.e

nm() 建立新的 Encrypt 物件,然後在訊息欄顯示密碼表。


由於重構後的 Encrypt 類別有 __str__() 方法,因此直接指派 Encrypt 物件給需要字串的地方,就會指派 __str__() 的回傳值。

來執行看看囉



這樣就整合好 Encrypt 類別了,接下來思考如何實作編碼與解碼有關的方法,雖說是用 Encrypt 類別的 toEncode() 方法進行編碼,但在 GUI 仍須考慮實際的情況。例如使用者 (user) 可能沒有輸入任何文字,或是沒有先按 New 建立新的 Encrypt 物件 (object) 就按下 Encode ,嗯,這都有可能發生無法執行的情況,甚至有可能導致程式 (program) 就此當掉。


實際上若是發生類似的情況, Python 直譯器 (interpreter) 還不致於當掉,倒是會發起例外 (exception) 結束或暫停程式執行,可是這樣程式跑起來就顯得卡卡的,總是給使用者一些不舒服的感覺。這樣先做個例外處理 (exception handling) 不外是個好的程式設計建議。


Python 用 try 偵測例外情況,但其實如果能不做例外處理就不做例外處理的好,為什麼呢?因為例外處理說起來讓直譯器先去檢查是否發生例外,這會讓程式執行上增加許多負擔,簡單說,需要額外的記憶體空間與處理器時間,也許在我們這樣的小程式看不出來,但是在大型程式中,這個缺點就顯而易見了。


不用例外處理的話,這樣的問題要解決也不難。因為 self.userinput 有設置初值為空字串,若使用者沒有輸入文字的話,從 self.ifd 取得的值也會是空字串,另外沒有新建 self.e 的話, self.e 的初值就是 None


因此,我們用 if-else 檢查可能的情況就可以了。編碼在 GUI 中為 em() 方法,程式如下


103# 按下 Encode 按鈕的事件 
104def em(self):
105   # 取得使用者輸入
106   self.userinput = self.ifd.get()
107
108   # 先測試使用者是否有輸入
109   if self.userinput == "":
110      m = "No input string!!"
111      self.dt["text"] = m
112   else:
113      # 再測試是否有按過 New 按鈕
114      if self.e == None:
115         m = "No encrypt object!!"
116         self.dt["text"] = m
117      else:
118         # 使用者有輸入
119         # 並且有按過 New 按鈕
120         s = self.userinput
121         r = self.e.toEncode(s)
122         self.result = r
123         self.ofd.delete(0, 200)
124         self.ofd.insert(0, r)
125         m = "Encoding success!!"
126         self.dt["text"] = m

如果使用者沒有輸入文字,訊息欄就顯示 "No input string!!" ,同樣的沒有新建 self.e ,訊息欄也提示相關訊息 "No encrypt object!!" ,而使用者有輸入文字亦有新建 Encrypt 物件的話,就把結果顯示到 self.ofd ,訊息欄就顯示 "Encoding success!!"


解碼方法 dm() 幾乎與 em() 完全相同,除了把 toEncode() 換成 toDecode() 之外


128# 按下 Decode 按鈕的事件 
129def dm(self):
130   # 取得使用者輸入
131   self.userinput = self.ifd.get()
132
133   # 先測試使用者是否有輸入
134   if self.userinput == "":
135      m = "No input string!!"
136      self.dt["text"] = m
137   else:
138      # 再測試是否有按過 New 按鈕
139      if self.e == None:
140         m = "No encrypt object!!"
141         self.dt["text"] = m
142      else:
143         # 使用者有輸入
144         # 並且有按過 New 按鈕
145         s = self.userinput
146         r = self.e.toDecode(s)
147         self.result = r
148         self.ofd.delete(0, 200)
149         self.ofd.insert(0, r)
150         m = "Decoding success!!"
151         self.dt["text"] = m

來執行看看囉!下面是編碼



下面是解碼



這樣的編碼效果還不錯,倒是我們想保存密碼表的話,就要實作存檔與載入的功能囉!


中英文術語對照


類別class
模組module
實體屬性instance attribute
物件object
字串string
方法method
使用者user
物件object
程式program
直譯器interpreter
例外exception
例外處理exception handling

重點整理


  1. 每個類別、模組的設計都應該要功能專一簡單化。
  2. __init__() 方法中的每個實體屬性都指派初值。


問題與討論


  1. 為什麼類別、模組的設計要功能專一簡單化?
  2. 為什麼在 __init__() 方法中要給實體屬性指派初值?

練習


  1. 承接上一個單元的 tk_demo2.py ,重新設定為 ButtonLabel 的互動模式,讓使用者第一次按鈕在 Label 顯示 "Click!" ,下一次按按鈕顯示 "What?" ,接下來依次 "Click!""What?" ....
  2. 承接上一個單元的 guessgame_gui.py ,整合之前發展的 guessgame 模組,讓使用者按下 New 後在 Label 顯示遊戲開始的提示訊息。

the end

沒有留言: