Python程式設計
Lesson 10: 類別與物件
「類別」(class) 與「物件」(object)是「物件導向」程式設計裡最重要的概念
類別
物件
身為資管人,學會運用「類別」與「物件」是精進程式技巧的不二法門
但是我不懂「物件導向」,Python程式也沒寫過幾個,程式又老是出錯...
別擔心!只要掌握類別與物件的基本用法,外加三大法門,你也可成為Python程式達人
建立類別(藍圖)
基本用法Fundamentals
從類別建立物件(按圖施工、完工)
使用物件(使用)
三大法門Design Tools for Class
封裝
繼承
多型
關鍵一:精熟類別(class)與物件(object) 的基本用法 (1)定義類別 (2)建立與使用物件 (3)使用建構元
類別
物件也叫實例
❶ 定義類別
物件BMW
物件Mercedes
❷ 建立
❸ 使用物件
┇
┅
精熟類別物件基本用法之1:建立類別(藍圖)
類別裡需定義物件的「性質」與「操作方式」
class University():
""" 定義大學類別 """
title = "真理大學" # 定義屬性
motto = "愛與服務" # 定義屬性
def vision(self): # 定義方法
return self.title +"的教學宗旨是" + self.motto
# 大學類別,無主程式
class Banks():
"""定義銀行類別"""
title = "淡水商業銀行" # 定義屬性
def motto(self): # 定義方法
return "服務至上"
# 銀行類別,無主程式
物件的性質-也就是「屬性」,代表該物件擁有的特徵,定義於類別內,方法外
class TShirt():
""" T恤類別 """
color = '紅色' # 顏色屬性
size = 'L' # 尺寸
price = 100 # 價格
def changeColor(self, 新顏色): # 方法: 改變顏色
self.color = 新顏色
練習1-1-1:定義一個商品的類別Product,該商品有名稱,介紹,價格,圖片四個屬性
「方法」是某個物件可操作的功能。定義於類別內。寫法雖與函式相同,但必須稱為「方法」
class TShirt():
""" T恤類別 """
color = '紅色' # 顏色屬性
size = 'L' # 尺寸
price = 100 # 價格
def changeColor(self, 新顏色): # 方法: 改變顏色
self.color = 新顏色
練習1-1-2:延續1-1的類別Product,該商品有名稱,介紹,價格,圖片四個屬性,請為類別Product加入三個「方法」:設定介紹文字、設定圖片、改變價格
提示:注意上例第6行的self不可省略
一個好的類別應該訂出適當的「屬性」與該有的操作「方法」
class Book():
""" 書籍類別 """
title = 'Python範例大全' #書名
author = '王小明' # 作者
price = 300 # 單價
存量 = 30 # 庫存數量
publisher = '真理出版社' # 出版社
def addBook(self, t, a , p, s, pub):
""" 新增一本書籍 """
self.title = t
self.author = a
self.price = p
self.存量 = s
self.publisher = pub
範例情境:網路書城的書籍管理
- 屬性:書名、作者、出版社、庫存數量、價格
- 方法:書籍的新增、刪除、修改
- 方法:根據價格範圍查詢書籍
此處只有一個方法,刪除? 修改? 查詢?
精熟類別物件基本用法之2:建立與使用物件。建立後,便可存取「屬性」與呼叫「方法」
class University():
""" 定義大學類別 """
title = '真理大學'
def motto():
return '愛與服務'
....
# 主程式開始
au = University() # 建立物件au,此物件是University類別
# au.title 可取得字串 '真理大學'
# au.motto() 可執行motto()方法
class Banks():
"""定義銀行類別"""
...
bank = Banks() # 建立銀行物件bank
...
物件變數 = 類別名稱()
建立物件
使用物件
物件變數.屬性名稱
物件變數.方法()
class University():
""" 定義大學類別 """
title = "真理大學" # 定義屬性
motto = "愛與服務" # 定義屬性
def vision(self): # 定義方法
return self.title +"的教學宗旨是" + self.motto
# 主程式開始
au = University() # 建立物件au,此物件是University這種類別
print("大學名稱是:", au.title)
print(au.vision())
「物件」建立後,透過「物件名稱」存取物件的屬性
練習1-2-1:延續類別Product,先建立一個Product類別的物件,緊接著印出該物件的名稱,介紹與價格
class TShirt():
""" T恤類別 """
color = '紅色' # 顏色屬性
size = 'L' # 尺寸
price = 100 # 價格
def changeColor(self, 新顏色): # 方法: 改變顏色
self.color = 新顏色
# 主程式
myT = TShirt()
print('原本的顏色', myT.color)
myT.changeColor('藍色')
print('新的顏色', myT.color)
「物件」建立後,透過「物件名稱」也可呼叫物件裡的方法
練習1-2-2:延續類別Product,先建立一個Product類別的物件,印出價格,緊接著從鍵盤輸入新的價格,修改物件裡的價格後,再印一次價格,看是否已經改變?
精熟類別物件基本用法之3:建構元。建構元是客製化「物件」的特殊函式,可直接設定物件初值
class TShirt():
""" T恤類別 """
color = '紅色' # 顏色屬性
size = 'L' # 尺寸
price = 100 # 價格
def changeColor(self, 新顏色): # 方法: 改變顏色
self.color = 新顏色
# 主程式
t1 = TShirt() # 第一件T恤
t2 = TShirt() # 第二件T恤
print(t1.color, t2.color) # 都是紅色
print(t1.size, t2.size) # 都是L
用TShirt()建立起來每件T恤都是「紅色」L號?
如何才能在建立物件,依需要設定初值? 建構元
__init__() 便是Python類別裡的建構元(1/3)
class TShirt():
""" T恤類別 """
price = 100 # 定義屬性price : 固定為100
def __init__(self, 顏色, 尺寸): # 左右各2個底線
"""建構元, 呼叫時必須給顏色與尺寸的初值"""
self.color = 顏色 # 定義屬性color 設定為參數'顏色'
self.size = 尺寸 # 定義屬性size 設定為參數'尺寸'
def changeColor(self, 新顏色): # 方法: 改變顏色
self.color = 新顏色
# 主程式
t1 = TShirt('紅色', 'L') # T恤1, 紅色L號
t2 = TShirt('藍色', 'M') # T恤2, 藍色M號
print(t1.color, t2.color)
print(t1.size, t2.size)
物件變數 = 類別名稱(屬性1初值, 屬性2初值...)
建構元使用方式
class University():
""" 定義大學類別 """
def __init__(self, t, m):
self.title = t # 定義屬性title
self.motto = m # 定義屬性motto
def vision(self): # 定義方法
return self.title +"的教學宗旨是" + self.motto
# 主程式開始
au = University('真理大學', '愛與服務') # 建立物件au
print("大學名稱是:", au.title)
print(au.vision())
範例:在建構元中定義title, motto兩屬性, 初值由參數t, m而來
__init__() 便是Python類別裡的建構元(2/3)
練習1-3-1:修改Product類別,加入建構元,並在建構元內設定所有初值
class Banks():
"""定義銀行類別"""
def __init__(self, name, money):
self.account = name # 定義屬性account,初值為name參數值
self.balance = money # 定義屬性balance,初值為money
self.title = '淡水商業銀行' # 定義屬性title, 初值固定
# 主程式
acc = Banks('王小明', 10000)
print(acc.title, '銀行帳戶明細')
print('帳戶%s的存款有%d' % (acc.account, acc.balance))
範例:在建構元中定義account, balance兩屬性
初值由參數name, money而來
也可同時有其他初值固定的屬性, 如title
__init__() 便是Python類別裡的建構元(3/3)
小結:類別與物件基本用法-類別應訂出適當的「屬性」與該有的操作「方法」
Employee類別
員工編號
員工姓名
getId(): 取得員工編號
getName(): 取得員工姓名
屬性
方法
Account類別
銀行名稱
帳號
帳戶餘額
getBalance(): 存款餘額
saveMoney(m): 存款withdraw(m): 提款
屬性
方法
屬性夠了嗎? 方法夠了嗎?
視程式需求而定!
class Account():
"""定義帳號類別"""
def __init__(self, bank, acc, money):
self.title = bank # 銀行名稱
self.account = acc # 帳號
self.balance = money # 餘額
def saveMoney(self, money):
"""存款"""
self.balance += money # 更新餘額
print('存款 %d 完成, 餘額 %d' % (money, self.balance ))
def withdraw(self, money):
"""提款"""
if (money <= self.balance):
self.balance -= money # 提款
print('提款 %d 完成, 餘額 %d' % (money, self.balance))
else: # 餘額不足
print('餘額不足, 目前餘額', self.balance)
def getBalance(self):
"""查詢餘額"""
return self.balance
acc = Account('第一銀行','Wang', 10000)
print('開戶銀行', acc.title)
print('目前餘額:', acc.getBalance())
acc.saveMoney(2000) # 存款
acc.withdraw(1000) # 提款
acc.withdraw(20000) # 餘額不足
Account類別: 3個屬性, 3個方法
class Employee():
"""員工類別"""
def __init__(self, id, 姓名):
self.eid = id # 屬性eid
self.name = 姓名 # 屬性name
def getId(self):
"""取得員工編號"""
return self.eid
def getName(self):
"""取得員工姓名"""
return self.name
e1 = Employee('E0001', '李小華')
e2 = Employee('E0002', '張小明')
print(e1.getName(), '的員工編號是:', e1.getId())
print(e2.getName(), '的員工編號是:', e2.getId())
Employee類別: 2個屬性, 2個方法
練習1-4-1:試著完成Product類別,加入新增產品,修改產品兩個方法
關鍵二:熟悉讓類別更具擴充彈性的三大法門—封裝、繼承與多型
類別
繼承
透過繼承「重複使用」屬性與方法
封裝
以權限「保護」類別的屬性與方法
多型
方法的「特化」(易於擴充、降低相依性)
關鍵二之一-物件導向三大法門:封裝(encapsulation)
封裝
屬性
方法
類別
封裝:類別(物件)外無法直接使用屬性或方法的「權限保護」機制
私有屬性:類別(物件)外無法直接修改的屬性。外部可修改,則稱為「公有屬性」
class Account():
"""定義帳號類別"""
def __init__(self, bank, acc, money):
self.title = bank # 銀行名稱
self.account = acc # 帳號
self.balance = money # 餘額
### ....省略.... ###
acc = Account('第一銀行','Wang', 10000)
# ...略....
# 未呼叫提款功能withdraw(),直接將餘額歸0
acc.balance = 0 # 外部可直接修改「公有屬性」
私有屬性:屬性名稱前加2底線
class Account():
"""定義帳號類別"""
def __init__(self, bank, acc, money):
self.__title = bank # 銀行名稱
self.__account = acc # 帳號
self.__balance = money # 餘額
### ....省略.... ###
acc.__balance = 0 # 此行將出現錯誤訊息
3個屬性都是公有屬性
class Account():
"""定義帳號類別"""
def __init__(self, bank, acc, money):
self.__title = bank # 銀行名稱
self.__account = acc # 帳號
self.__balance = money # 餘額
def saveMoney(self, money):
self.__balance += money # 更新餘額
print('存款 %d 完成, 餘額 %d' % (money, self.__balance ))
def withdraw(self, money):
if (money <= self.__balance):
self.__balance -= money # 提款
print('提款 %d 完成, 餘額 %d' % (money, self.__balance))
else: # 餘額不足
print('餘額不足, 目前餘額', self.__balance)
def getBalance(self):
return self.__balance
def getTitle(self):
return self.__title
acc = Account('第一銀行','Wang', 10000)
print('開戶銀行', acc.getTitle())
print('目前餘額:', acc.getBalance())
acc.saveMoney(2000) # 存款
acc.withdraw(1000) # 提款
acc.withdraw(20000) # 餘額不足
Account類別: 3個私有屬性,多了getTitle()方法
class Employee():
"""員工類別"""
def __init__(self, id, 姓名):
self.__eid = id # 屬性eid
self.__name = 姓名 # 屬性name
def getId(self):
"""取得員工編號"""
return self.__eid
def getName(self):
"""取得員工姓名"""
return self.__name
e1 = Employee('E0001', '李小華')
e2 = Employee('E0002', '張小明')
print(e1.getName(), '的員工編號是:', e1.getId())
print(e2.getName(), '的員工編號是:', e2.getId())
Employee類別: 2個私有屬性
練習2-1-1:修改Product類別,將價格與名稱改為私有屬性,留意其他方法是否也需同步修改
私有方法:類別(物件)外無法直接呼叫的方法。外部可呼叫,則稱為「公有方法」
class Account():
"""定義帳號類別"""
def __init__(self, bank, acc, money):
self.__title = bank # 銀行名稱
self.__account = acc # 帳號
self.__balance = money # 餘額
self.__rate = 30.5
# 公有方法
def exchange(self, usd):
ntd = self.__calulate(usd)
return ntd
# 私有方法:名稱前加兩底線
def __calulate(self, money):
return money * self.__rate
acc = Account('第一銀行','Wang', 10000)
美元 = int(input('輸入要換的金額(美元)'))
台幣=acc.exchange(美元)
print('美元%d 可換台幣%8.2f' % (美元, 台幣))
私有方法:方法名稱前加2底線
私有方法在外部不能直接呼叫
關鍵二之二-物件導向三大法門:繼承(Inheritance)
父類別/基底類別
公有屬性
公有方法
子類別/衍生類別
公有屬性
公有方法
繼承
自訂其他屬性與方法
擴充
子類別建立時,以父類別為參數。故子類別必須寫在父類別之後
class Employee():
"""員工類別"""
def __init__(self, id, 姓名):
self.eid = id # 屬性eid
self.name = 姓名 # 屬性name
def getId(self):
"""取得員工編號"""
return self.eid
def getName(self):
"""取得員工姓名"""
return self.name
class ACompany(Employee):
pass # 不做任何事情,所有程式繼續
a1 = ACompany('E0001', '李小華')
a2 = ACompany('E0002', '張小明')
print(a1.getName(), '的員工編號是:', a1.getId())
print(a2.getName(), '的員工編號是:', a2.getId())
子類別(以父類別做為參數)
父類別
class Car():
""" 基底類別 """
def __init__(self, w=4, d=4, s=5):
self.wheel = w # 公有屬性: 輪胎數量
self.door = d # 公有屬性: 車門數量
self.seat = s # 公有屬性: 座位數量
myCar = Car() # 預設值 4輪4門5人座
print('幾門:', myCar.door)
urCar = Car(4, 5, 7) # 4輪 5門 7人座
print('座位數:',urCar.seat)
Car類別
wheel
door
seat
無公有方法
Mpv類別
繼承
擴充
wheel
door
seat
無公有方法
air_bag屬性
brand屬性
getDetails()方法
class Car():
""" 父類別 / 基底類別 """
def __init__(self, w=4, d=4, s=5):
self.wheel = w # 公有屬性: 輪胎數量
self.door = d # 公有屬性: 車門數量
self.seat = s # 公有屬性: 座位數量
class Mpv(Car):
"""子類別 / 衍生類別"""
air_bag = 2
brand = ""
def __init__(self, bag=2, brand='Toyota'):
super().__init__()
self.air_bag = bag
self.brand = brand
def getDetails(self):
print('廠牌:', self.brand)
print('氣囊:', self.air_bag)
print('輪子:', self.wheel)
print('車門:', self.door)
print('座位:', self.seat)
myCar = Mpv() # 預設值 4輪4門5人座, 2氣囊, Toyota
myCar.getDetails()
練習2-2-1:建立地點父類別,有「住址」,經度,緯度三個屬性,另建立子類別景點,擴充屬性「名稱」, 「類型」,最後再擴充方法,該方法能印出子類別所有屬性值
關鍵二之三-物件導向三大法門:多型(Polymorphism)
多型 就是 一字多義 的概念
drive() 方法
每一種交通工具drive方式各不相同,但都叫drive():多型
抽象化(Abstraction)是多型的基本要素。
from abc import ABC, abstractmethod
class AbstractClass(ABC):
@abstractmethod
def myMethod(self):
pass
抽象方法:只有宣告、不實作內容
抽象類別是一個空架子
內含一或多個抽象方法
但有提供「抽象基底類別」在abc模組裡,類別名字叫做ABC(Abstract Base Class)
Python也在abc模組裡,提供「抽象方法」修飾指令@abstractmethod
Python沒有Abstract Class語法
from abc import ABC, abstractmethod
class AbstractClassExample(ABC): # 定義抽象類別
@abstractmethod # 內有一抽象方法
def do_something(self): # 抽象方法的實作內容(必要)
print("Some implementation!")
class AnotherSubclass(AbstractClassExample): # 繼承上述抽象類別
# 必須「實作」抽象類別所有定義的「抽象方法」
def do_something(self): # 抽象方法實作(會覆蓋原先內容)
super().do_something() # super() 父類別
print("The enrichment from AnotherSubclass")
x = AnotherSubclass() # 建構元:建立物件
x.do_something() # 使用物件
Python程式設計
By Leuo-Hong Wang
Python程式設計
Lesson 10: 類別與物件
- 974