### 7.2.1 類的定義
利用 OOP 來解決問題時,首要任務是確定該問題涉及哪些對象,每種對象分別具有什 么數據和操作。類就是對這些信息的表達。類的創建,體現了面向對象的諸多思想和方法, 本節對此進行詳細介紹。
抽象
人們在認識客觀世界時,經常采用抽象方法來對客觀世界的眾多事物進行歸納、分類。 抽象就是忽略事物中與當前目標無關的、非本質的特征,而抽取與當前目標有關的、本質的特征。經過抽象,能夠確認事物間的共性,并將具有共性的事物歸入一類,從而得到一個抽 象概念。抽象時需要考慮“當前目標”,對于某個事物,根據不同的解題目標,可以進行不同 的抽象。
抽象是面向對象的基本方法,而抽象的工具就是“類”。我們用類來抽象、描述待求解 問題所涉及的事物,具體包括兩個方面的抽象:數據抽象和行為抽象。數據抽象描述某類對 象共有的屬性或狀態,行為抽象描述某類對象共有的行為或功能特征。
例如,對學校里一個個具體的學生張三、李四、王五等進行概括,可以抽取出學號、姓 名等數據屬性,還可以抽取出選課、做項目、加入學生社團等行為屬性,從而建立“學生類”。 又如,對馬路上形形色色的汽車進行概括,可以抽取出制造商、型號、排量等數據屬性,還 可以抽取出啟動、剎車、加速等行為屬性,從而建立“汽車類”。
抽象可以是在多個層次上進行的。例如,當抽象出學生、教師、職工等類別之后,可以 從他們進一步抽象出“師生員工”類;當建立了汽車、火車、飛機等類別之后,可以從他們 進一步抽象出“交通工具”類。如此進行,最終可以形成一個抽象層次,稱為類層次。這種 抽象方法在各學科中都是常用的,最典型的如生物學中的分類層次:
```
智人 → 人科 → 靈長目 → 哺乳綱 → 脊索動物門 → 動物界
```
封裝
在日常生活中,人們使用著各種提供特定服務的設備,如汽車、彩電等等。對于這些設備,人們通常只需了解它們的功能,而不關心它們的內部是怎樣工作的。類似地,計算機程 序也是提供特定服務的“設備”,我們使用程序時通常也只關心程序的功能,而不關心程序內 部的實現過程。OOP 中的封裝概念正是這種思想的體現。
封裝(encapsulation)是指用類將對象的數據和操作結合成一個封閉的程序單元,并對 外部隱藏內部實現細節。隱藏信息包含兩重意思:一是用戶無需了解隱藏的信息就能使用該 類,二是不允許用戶直接操作類中被隱藏的信息。
封裝導致外界不能直接存取對象的數據,但這并不是說我們就無法處理對象數據了。與 數據封裝在一起的還有對數據的操作(稱為方法的一些函數),類會對外公開這些方法的名稱 和調用格式,亦即提供了與外界的交互界面。外界可以向對象發消息(所請求的方法名稱及 參數),然后對象通過執行方法來響應外界的消息。所以,外界可通過消息機制來間接處理對 象數據。當然,外界只能按對象允許的方式來處理對象數據,因為對象能夠響應的消息是由 類定義決定的。
封裝對類的實現者和使用者都有好處。第一,防止使用者直接操作對象數據時有意無意 造成的錯誤,畢竟對象自己的方法處理自己的數據才是安全的;第二,使用者通過方法調用 來操作數據時無需了解內部實現細節,類的功能非常易用;第三,即使實現者以后修改了類 的內部實現,只要不改變方法界面,使用者就不會受到任何影響,這使得程序非常容易維護; 第四,可以使同類甚至不同類的對象對使用者都呈現標準化的操作界面。以電視機為例可以 很清楚地看出上述優點。電視機將內部各種器件封裝起來,使用戶只能通過面板上的按鍵來 操作電視機,這樣既保護了內部器件使之不易被用戶損壞,又使電視機簡單易用;電視機內 部器件的維修和升級對用戶來說沒有任何影響;所有品牌的電視機幾乎都提供標準化的面板(電源開關、頻道切換、音量調節等),只要會使用一種電視機,基本上就會使用其他任何電 視機甚至收音機(因為收音機界面也是類似的調換頻道、調節音量等)。
Python 類定義
如前所述,類是用來刻劃對象的數據特性和行為特性的。Python 中類定義形如:
```
class <類名>:
<方法定義 1>:
...
<方法定義 n>:
```
其中諸“方法定義”就是對對象的操作行為的定義,外界能夠請求對象所做的事情完全由這 些方法決定。
每個方法定義其實都是一個函數定義,即形如:
```
def <方法名>(<參數>):
...
```
方法與普通函數略有差別。首先,每個方法都必須有一個特殊的形參 self,并且 self 必 須是該方法的第一個形參(如果還有其他參數的話)。至于這個特殊參數的作用,我們在稍后 介紹方法調用的時候再解釋。其次,方法只能通過對象來調用,即向對象發消息請求對象執 行某個方法。
至此,讀者也許會有疑問:不是說類中包含了數據和操作的定義嗎,怎么上述類定義形 式中只有操作沒有數據?在 Python 中,類的實例所擁有的數據一般在類的方法中定義的,尤 其是在一個特殊方法 init__方法①中。這里的方法名 init__是 Python 規定的,通常每個類 都會定義 init__方法,其功能將在介紹類實例創建時進行解釋。
作為例子,我們來定義類 Person。Person 是對現實世界中“人”的抽象,每個人都有自 己的姓名和出生年份,并且都能回答外界有關其姓名和年齡的提問。我們將 Person 類的定義 保存在文件 person.py 中,將來任何程序都可以通過導入此文件而使用 Person 類。
【程序 7.3】person.py
```
class Person:
def __init__ (self,n,y):
self.name = n self.year = y
def whatName(self):
print "My name is",self.name
def howOld(self,y):
age = y – self.year
if age > 0:
print "My age in",y,"is",age
else:
print "I was born in",self.year
```
Person 對象的數據就是在 init__方法中定義的 self.name 和 self.year,Person 對象能夠執 行的操作就是方法 whatName()和 howOld()。
像 Person 類中的 self.name 和 self.year 這樣,以“self.<變量名>”形式定義的變量稱為實 例變量(instance variable),意思是這種數據是屬于類的實例的,不同實例可有不同的值。實 例變量可以在類的任何方法中定義(通常在 init__中定義),也可以在類的任何方法中直接 引用。Person 類中,self.name 和 self.year 是實例變量,將來創建的每一個 Person 實例都擁有 這兩個數據,每個實例各自的姓名和出生年份的值可以是不同的;另外,這兩個實例變量雖 然是在 init__中定義,但在 whatName 和 howOld 方法中都可以直接引用。注意,如果方法 中定義的某個變量名前沒有 self 前綴,則該變量是普通的函數局部變量,其作用域僅限于該方法。例如上例中在 howOld 方法中定義的 age 就是局部變量,它只能在 howOld 中引用。 類的方法定義中可以通過 self.f()的方式調用同一個類中的其它方法 f。例如:
> ① 注意,這個名稱中 init 前后各是兩個下劃線字符!Python 常用這種形式的標識符來命名內部對象。
```
class Person1:
def __init__ (self,n,y):
self.name = n
self.year = y
def whatName(self):
print "My name is",self.name
def howOld(self,y):
age = y – self.year
if age > 0:
print "My age in",y,"is",age
else:
print "I was born in",self.year
def allInfo(self,y):
self.whatName()
self.howOld(y)
```
其中的方法 allInfo 調用了本類中的另外兩個方法 whatName 和 howOld。
類實際上是用戶自定義的數據類型,是對 Python 語言本身提供的基本數據類型的擴充。 一旦定義了類,就可以將它當作 Python 標準數據類型來使用。
作為特例,如果某種對象只包含一些數據,不包含對數據的操作,則可以在類中只定義 實例變量而不定義操作方法(除了特殊的 init 方法)。這樣的類相當于很多編程語言中的 “結構”類型①,其作用是將一些數據項組合成一個整體。事實上,類可以看作是傳統結構 類型的發展,即類是添加了數據操作的“結構”。
最后說一下命名問題。面向對象編程中習慣上使用大寫字母開頭的標識符來為類命名(如 Person),而類中方法和實例變量則慣常使用小寫字母開頭的標識符來命名(如 name、year 和 whatName)。方法名如果由多個單詞構成,一般采用駱駝式命名法,即除第一個單詞之外 的各單詞首字母大寫,如 whatName 和 howOld。
- 前言
- 第 1 章 計算與計算思維
- 1.1 什么是計算?
- 1.1.1 計算機與計算
- 1.1.2 計算機語言
- 1.1.3 算法
- 1.1.4 實現
- 1.2 什么是計算思維?
- 1.2.1 計算思維的基本原則
- 1.2.2 計算思維的具體例子
- 1.2.3 日常生活中的計算思維
- 1.2.4 計算思維對其他學科的影響
- 1.3 初識 Python
- 1.3.1 Python 簡介
- 1.3.2 第一個程序
- 1.3.3 程序的執行方式
- 1.3.4 Python 語言的基本成分
- 1.4 程序排錯
- 1.5 練習
- 第 2 章 用數據表示現實世界
- 2.1 數據和數據類型
- 2.1.1 數據是對現實的抽象
- 2.1.1 常量與變量
- 2.1.2 數據類型
- 2.1.3 Python 的動態類型*
- 2.2 數值類型
- 2.2.1 整數類型 int
- 2.2.2 長整數類型 long
- 2.2.3 浮點數類型 float
- 2.2.4 數學庫模塊 math
- 2.2.5 復數類型 complex*
- 2.3 字符串類型 str
- 2.3.1 字符串類型的字面值形式
- 2.3.2 字符串類型的操作
- 2.3.3 字符的機內表示
- 2.3.4 字符串類型與其他類型的轉換
- 2.3.5 字符串庫 string
- 2.4 布爾類型 bool
- 2.4.1 關系運算
- 2.4.2 邏輯運算
- 2.4.3 布爾代數運算定律*
- 2.4.4 Python 中真假的表示與計算*
- 2.5 列表和元組類型
- 2.5.1 列表類型 list
- 2.5.2 元組類型 tuple
- 2.6 數據的輸入和輸出
- 2.6.1 數據的輸入
- 2.6.2 數據的輸出
- 2.6.3 格式化輸出
- 2.7 編程案例:查找問題
- 2.8 練習
- 第 3 章 數據處理的流程控制
- 3.1 順序控制結構
- 3.2 分支控制結構
- 3.2.1 單分支結構
- 3.2.2 兩路分支結構
- 3.2.3 多路分支結構
- 3.3 異常處理
- 3.3.1 傳統的錯誤檢測方法
- 3.3.2 傳統錯誤檢測方法的缺點
- 3.3.3 異常處理機制
- 3.4 循環控制結構
- 3.4.1 for 循環
- 3.4.2 while 循環
- 3.4.3 循環的非正常中斷
- 3.4.4 嵌套循環
- 3.5 結構化程序設計
- 3.5.1 程序開發過程
- 3.5.2 結構化程序設計的基本內容
- 3.6 編程案例:如何求 n 個數據的最大值?
- 3.6.1 幾種解題策略
- 3.6.2 經驗總結
- 3.7 Python 布爾表達式用作控制結構*
- 3.8 練習
- 第 4 章 模塊化編程
- 4.1 模塊化編程基本概念
- 4.1.1 模塊化設計概述
- 4.1.2 模塊化編程
- 4.1.3 編程語言對模塊化編程的支持
- 4.2 Python 語言中的函數
- 4.2.1 用函數減少重復代碼 首先看一個簡單的用字符畫一棵樹的程序:
- 4.2.2 用函數改善程序結構
- 4.2.3 用函數增強程序的通用性
- 4.2.4 小結:函數的定義與調用
- 4.2.5 變量的作用域
- 4.2.6 函數的返回值
- 4.3 自頂向下設計
- 4.3.1 頂層設計
- 4.3.2 第二層設計
- 4.3.3 第三層設計
- 4.3.4 第四層設計
- 4.3.5 自底向上實現與單元測試
- 4.3.6 開發過程小結
- 4.4 Python 模塊*
- 4.4.1 模塊的創建和使用
- 4.4.2 Python 程序架構
- 4.4.3 標準庫模塊
- 4.4.4 模塊的有條件執行
- 4.5 練習
- 第 5 章 圖形編程
- 5.1 概述
- 5.1.1 計算可視化
- 5.1.2 圖形是復雜數據
- 5.1.3 用對象表示復雜數據
- 5.2 Tkinter 圖形編程
- 5.2.1 導入模塊及創建根窗口
- 5.2.2 創建畫布
- 5.2.3 在畫布上繪圖
- 5.2.4 圖形的事件處理
- 5.3 編程案例
- 5.3.1 統計圖表
- 5.3.2 計算機動畫
- 5.4 軟件的層次化設計:一個案例
- 5.4.1 層次化體系結構
- 5.4.2 案例:圖形庫 graphics
- 5.4.3 graphics 與面向對象
- 5.5 練習
- 第 6 章 大量數據的表示和處理
- 6.1 概述
- 6.2 有序的數據集合體
- 6.2.1 字符串
- 6.2.2 列表
- 6.2.3 元組
- 6.3 無序的數據集合體
- 6.3.1 集合
- 6.3.2 字典
- 6.4 文件
- 6.4.1 文件的基本概念
- 6.4.2 文件操作
- 6.4.3 編程案例:文本文件分析
- 6.4.4 緩沖
- 6.4.5 二進制文件與隨機存取*
- 6.5 幾種高級數據結構*
- 6.5.1 鏈表
- 6.5.2 堆棧
- 6.5.3 隊列
- 6.6 練習
- 第 7 章 面向對象思想與編程
- 7.1 數據與操作:兩種觀點
- 7.1.1 面向過程觀點
- 7.1.2 面向對象觀點
- 7.1.3 類是類型概念的發展
- 7.2 面向對象編程
- 7.2.1 類的定義
- 7.2.2 對象的創建
- 7.2.3 對象方法的調用
- 7.2.4 編程實例:模擬炮彈飛行
- 7.2.5 類與模塊化
- 7.2.6 對象的集合體
- 7.3 超類與子類*
- 7.3.1 繼承
- 7.3.2 覆寫
- 7.3.3 多態性
- 7.4 面向對象設計*
- 7.5 練習
- 第 8 章 圖形用戶界面
- 8.1 圖形用戶界面概述
- 8.1.1 程序的用戶界面
- 8.1.2 圖形界面的組成
- 8.1.3 事件驅動
- 8.2 GUI 編程
- 8.2.1 UI 編程概述
- 8.2.2 初識 Tkinter
- 8.2.3 常見 GUI 構件的用法
- 8.2.4 布局
- 8.2.5 對話框*
- 8.3 Tkinter 事件驅動編程
- 8.3.1 事件和事件對象
- 8.3.2 事件處理
- 8.4 模型-視圖設計方法
- 8.4.1 將 GUI 應用程序封裝成對象
- 8.4.2 模型與視圖
- 8.4.3 編程案例:匯率換算器
- 8.5 練習
- 第 9 章 模擬與并發
- 9.1 模擬
- 9.1.1 計算機建模
- 9.1.2 隨機問題的建模與模擬
- 9.1.3 編程案例:乒乓球比賽模擬
- 9.2 原型法
- 9.3 并行計算*
- 9.3.1 串行、并發與并行
- 9.3.2 進程與線程
- 9.3.3 多線程編程的應用
- 9.3.4 Python 多線程編程
- 9.3.5 小結
- 9.4 練習
- 第 10 章 算法設計和分析
- 10.1 枚舉法
- 10.2 遞歸
- 10.3 分治法
- 10.4 貪心法
- 10.5 算法分析
- 10.5.1 算法復雜度
- 10.5.2 算法分析實例
- 10.6 不可計算的問題
- 10.7 練習
- 第 11 章 計算+X
- 11.1 計算數學
- 11.2 生物信息學
- 11.3 計算物理學
- 11.4 計算化學
- 11.5 計算經濟學
- 11.6 練習
- 附錄
- 1 Python 異常處理參考
- 2 Tkinter 畫布方法
- 3 Tkinter 編程參考
- 3.1 構件屬性值的設置
- 3.2 構件的標準屬性
- 3.3 各種構件的屬性
- 3.4 對話框
- 3.5 事件
- 參考文獻