5. 從模組到類別
• 學習目標
– 深入模組管理
– 初識物件導向
– 學習定義類別
– 定義運算子
架構程式
• 抽象層的封裝與隔離
• 物件的狀態
• 名稱空間
• 資源實體組織方式
用模組建立抽象層
• 一個 .py 檔案就是一個模組
• 模組成為 Python 中最自然的抽象層
• 想要知道一個模組中有哪些名稱,可以使
用 dir() 函式
• import 某個模組,指定的 .py 檔案載入
• python 直譯器會為它建立一個 module
實例,並建立一個模組名稱來參考它
• 銀行商務相關的簡單程式
• 透過 bank 模組名稱來進行相關的商務流程
• from import 會將被匯入模組中之名稱參
考的值,指定給目前模組中建立的新名稱
• 如果有些變數,並不想被 from import
* 建立同名變數,可以用底線作為開頭
• 例如,若 foo.py 中有以下內容:
• 定義 __all__ 清單,使用字串列出可被
from import * 的名稱:
• 若模組定義了__all__ 變數,名單中的變
數,才可以被其他模組 from import *
• 無論是底線開頭,或者是未被列入
__all__ 清單的名稱,只是限制不被
from import *
• 若使用者 import foo,依舊可以使用
foo._y 或 foo.z 來存取
• 可以使用 del 將模組名稱或者 from
import 的名稱刪除
• 模擬 import foo as qoo
• del 是用來刪除指定的名稱,而不是刪除
名稱參考的物件本身
• 想要知道目前已載入的 module 名稱與實
例有哪些,可以透過 sys.modules
• 能根據不同的情況進行不同的 import
設定 PTH 檔案
• 尋找模組時的來源
– 執行 python 直譯器時的資料夾
– PYTHONPATH 環境變數
– Python 安裝中標準程式庫等資料夾
– PTH 檔案列出的資料夾
• 在一個 .pth 檔案中列出模組搜尋路徑
• PTH 檔案的位置,不同作業系統並不相同
• 可透過 site 模組的 getsitepackages()
函式取得
• 如果確實建立了workspace.pth 中列出的
資料夾,而且將 workspace.pth 放置到
C:Program Files (x86)Python35-32
• 如果將 workspace.pth 放置到C:Program
Files (x86)Python35-32libsite-
packages
• 如果想將 PTH 檔案放置到其他資料夾,可
以使用 site.addsitedir() 函式新增
PTH 檔案的資料夾來源
初識物件導向
• 何時該以物件來思考或組織應用程式行為
呢?
• 可以從打算將物件的狀態與功能黏在一起
時開始…
• 可以將初始化流程,使用 __init__() 方
法定義在類別之中
• 方法前後各有兩個連線底線,意謂著在類
別以外的其他位置,不要直接呼叫
• 基本上都會有個函式可用來呼叫這類方法
• 在呼叫 __init__() 方法時,建立的
Account 實例會傳入作為方法的第一個參
數
• Python 的慣例中,第一個參數的名稱會命
名為 self
• 將 deposit() 以及 withdraw() 也定義
在 Account 類別之中
• 物件的方法第一個參數一定是物件本身
• 傳回物件描述字串的方法
• 若執行 str(acct) 時,就會呼叫 acct 的
__str__() 方法取得描述字串並傳回
• 類別中也可以定義 __repr__() 方法
• 當執行 repr(acct) 時,就會呼叫 acct
的 __repr__() 方法取得描述字串並傳回
• __str__() 字串描述是給人類看的
• __repr__() 是給程式、機器剖析用的特
定格式時,或者是包含除錯用的字串資訊
• 可能會有人如下誤用:
• 如果想要避免使用者直接的誤用,可以使
用 self.__xxx 的方式定義內部值域
• 不能使用 acct.__name、
acct.__number、acct.__balance 進
行屬性存取,這會引發 AttributeError
• 使用者仍然可以用另一種方式來存取:
• 基本上,可以直接定義一些方法來傳回內
部屬性的值
• 可以考慮在這類方法上加註 @property
• 可以使用 acct.name、acct.number、
acct.balance 的形式取得值
• 想要進一步提供 acct.balance =
10000 這樣的形式
• 被 @property 標註的 xxx 取值方法可以
使用@xxx.setter 標註
• 使用 @xxx.deleter 來標註對應的刪除值
之方法
• 取值方法傳回的值可以是即時運算的結果,
設值方法必要時可以使用流程語法等來實
作一些存取控制
綁定與未綁定方法
• 如果試著將 acct.deposit 或
acct.withdraw 指定給一個變數:
• 如果在類別中定義了一個方法,沒有任何
參數會怎樣呢?
• 可以透過類別名稱來呼叫它,或取得函式
物件進行呼叫:
• 取得綁定方法綁定的物件:
靜態方法與類別方法
• 當呼叫 acct.deposit(500) 時,會將
acct 參考的實例傳給 deposit() 的第一
個 self 參數,也可以如下取得相同效果:
• 如果想要有類似 deposit =
acct.deposit 的效果:
• 在定義類別時, 希望某個方法不被拿來作
為綁定方法, 可以使 @staticmethod
• acct.default(‘Monica’, ‘765-
4321’),acct 也不會傳入作為
default() 的首個參數
• 建議透過類別名稱來呼叫,明確地讓類別
名稱作為靜態方法的名稱空間
• 在 Python 中定義的類別,也會產生對應的
物件,這個物件會是 type 的實例
• 類別中的方法若標註了@classmethod,
那麼第一個參數一定是接受所在類別的
type 實例
• 必要的話,一個自定義的類別實例,也可
以作為名稱空間
• 每個物件本身,都會有個 __dict__ 屬性,
當中記錄著類別或實例所擁有的特性
• 若想取得 __dict__ 的資料,其實可以使
用 vars() 函式
• 若函式或變數以類別為名稱空間,建議透
過類別名稱來呼叫或存取
• 語義上比較清楚,一眼就可以看出函式或
變數是以類別為名稱空間
• 還可以避免以下的問題:
• 在類別上直接新增方法:
• del 可用來刪除變數,或已匯入目前模組
的名稱,也可用來刪除某個物件上的屬性
• 模組也是個物件,也可以使用 del 來刪除
模組上定義的名稱
• del 真正的作用是刪除某物件上的屬性
定義運算子
• 建立一個有理數類別
• 類別的實例如何建構,實際上是由
__new__() 方法來定義
• 由於 __new__() 若傳回第一個參數的類別
實例,就會執行 __init__() 方法
• 藉由定義 __new__() 方法,就可以決定如
何建構物件與初始物件
• 想在物件被刪除時,自行定義一些清除相
關資源的行為,可以實作 __del__() 方法

從模組到類別