iT邦幫忙

2025 iThome 鐵人賽

DAY 21
0

前言

昨天我們用函式做了一個簡單的 ATM 模擬器

  • 可以查詢餘額
  • 存錢
  • 領錢
  • 換匯
  • 結束交易

雖然有點像真的 ATM,但其實隱藏了一些小危機喔:

如果有 10 個人要用 ATM,每個人都有不同的名字和不同的餘額,
我們就要寫一大堆變數來存放這些人。
每次操作時,還要記得傳「餘額」進去函式裡,容易搞混。

這時候,就要用到
物件導向程式設計(Object-Oriented Programming,簡稱 OOP)

ㄧ、什麼是「物件導向」?

我們先用生活中的例子來解釋:

  • 想像「房子的設計圖」 → 這就是 類別(Class)
    它定義了一個房子長什麼樣子,有幾個房間,幾扇窗戶。

  • 用設計圖蓋出來的「真正的房子」 → 這就是 物件(Object)
    每一棟房子雖然長得差不多,但都有自己的顏色、大小、住的人。

我們把上面說的概念切換到程式世界:

  • 類別就是「藍圖」,規定了帳戶有什麼資訊、能做什麼事。
  • 物件就是「真的帳戶」,小明有小明的錢包,小華有小華的錢包,互不影響。

這樣說可能還是會有點抽象,那我們來實際看個例子吧!

(1)建立第一個帳戶類別

我們來設計一個帳戶的藍圖,取名叫 Account

class Account:
    # 建構子(初始化帳戶)
    def __init__(self, name, balance):
        self.name = name        # 帳戶擁有者
        self.balance = balance  # 餘額
    
    # 查詢餘額
    def check_balance(self):
        print(f"{self.name} 的餘額是:{self.balance} 元")
    
    # 存錢
    def deposit(self, amount):
        self.balance += amount
        print(f"{self.name} 存了 {amount} 元,現在餘額是:{self.balance} 元")
    
    # 領錢
    def withdraw(self, amount):
        if amount <= self.balance:
            self.balance -= amount
            print(f"{self.name} 領了 {amount} 元,現在餘額是:{self.balance} 元")
        else:
            print("餘額不足!")

哇哇哇,是不是有看到熟悉的def(函式)!

這邊我來用比較白話一點的方式講解~

  • class Account: → 建立一個類別,名字叫 Account

  • __init__ → 這是一個「建構子」,每次我們要建立帳戶時,這個函式會自動執行。

  • self.name:代表帳戶的「名字」。

  • self.balance:代表帳戶的「餘額」。

  • self → 意思是「這個物件自己」。

  • 如果有 100 個帳戶,每個帳戶都有自己的 namebalanceself 幫你區分誰是誰。

  • depositwithdraw → 就是帳戶能做的動作,我們把它叫做 方法(method)

(2)建立物件並操作

範例:

# 建立兩個帳戶
acc1 = Account("小明", 1000)   # 小明的帳戶,起始金額 1000
acc2 = Account("小華", 500)    # 小華的帳戶,起始金額 500

# 查詢餘額
acc1.check_balance()   # 小明查詢餘額
acc2.check_balance()   # 小華查詢餘額

# 存錢
acc1.deposit(300)      # 小明存 300 元

# 領錢
acc2.withdraw(600)     # 小華領 600 元

輸出:
https://ithelp.ithome.com.tw/upload/images/20251005/20164721hrxFYlo9hd.png

很酷吧?現在每個帳戶都「自己記得」自己的餘額,程式變得清楚很多!

第一次接觸物件導向的讀者們可能會覺得有一點點抽象,
沒關係!我們來一步步分解我們剛剛的程式碼!

基礎先打好,明天接觸更進階的才會更順利~

什麼是 __init__

先看一段程式碼:

class Account:
    def __init__(self, name, balance):
        self.name = name
        self.balance = balance

這邊有個關鍵字:__init__(兩邊各有兩個底線)。

  • 它叫做 建構子(constructor)
  • 當我們「建立物件」的時候,__init__ 就會自動跑一次,幫我們把物件的資料準備好。

舉例來說:

acc1 = Account("小明", 1000)

這行的意思是:

  1. 建立一個新的帳戶物件 acc1
  2. 自動呼叫 __init__,把 "小明" 塞進 self.name,把 1000 塞進 self.balance
  3. acc1 就會帶著「小明」和「1000 元」誕生。

你可以把 __init__ 想成「帳戶開戶表單」,填進去的資料就是每個帳戶的獨特資訊。

self 又是什麼?

  • self 是一個「自我指標」。
  • 當小明建立帳戶時,self 代表「小明的帳戶」。
  • 當小華建立帳戶時,self 代表「小華的帳戶」。
self.name = name
self.balance = balance

意思就是:
「把傳進來的名字和金額,分別存到這個帳戶物件的 name 和 balance裡。」

如何呼叫方法?(用「點」.

當我們建立好帳戶物件後,就可以用「點」. 來呼叫方法。
(會不會太白話哈哈)

acc1 = Account("小明", 1000)
acc1.check_balance()
acc1.deposit(500)

這裡的acc1代表「小明的帳戶」,所以acc1代表「小明的帳戶」,

所以acc1.check_balance()的意思是:
「讓小明這個帳戶,執行check_balance這個動作。」

如果是小華:

acc2 = Account("小華", 500)
acc2.withdraw(200)

就是「小華這個帳戶,執行 withdraw 這個動作」。
小明和小華雖然用同一個類別 Account 建立,
但各自的餘額是分開的,互不影響。

接下來我會介紹一下我當初在學物件導向這邊,
常常混淆的地方:

二、新手常見混淆&踩雷區

1. self 必須寫,但不用傳

很多人以為呼叫方法時要自己傳 self,例如:

acc1.deposit(self, 500)  # ❌ 錯誤!

其實不用這樣寫!因為Python 會自動把物件自己傳進 self

正確的寫法:

acc1.deposit(500)  

2.__init__ 名字一定要打對

有些人會不小心少打底線,變成 init,結果 Python 根本不會自動執行。

要記住:前後各兩條底線__init__

3.物件之間的資料不會互相影響

acc1 = Account("小明", 1000)
acc2 = Account("小華", 500)
acc1.deposit(200)
print(acc2.balance)  # 還是 500,不會被改到

很多新手會誤以為「存錢」會影響到所有帳戶,這是不對的。
每個物件都是獨立的!(這也是物件導向的優點!)

三、實戰演練

範例1:商品與購物車

模擬線上購物,每個商品都是「物件」,購物車可以顯示總金額。

完成這題練習題可以讓讀者更了解:

1.如何用 class 定義物件(商品、購物車)
2.物件之間如何互動(購物車裝商品)
3.init、self 的用途
4.如何用方法(method)設計功能,如加入商品、計算總價

那話不多說!我們一起來看看吧!!

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

class Cart:
    def __init__(self):
        self.items = []
    
    def add_item(self, product):
        self.items.append(product)
        print(f"加入 {product.name} (${product.price}) 到購物車")
    
    def total(self):
        total_price = sum(item.price for item in self.items)
        print(f"購物車總金額:${total_price}")

# 建立商品
apple = Product("蘋果", 30)
book = Product("書", 120)
milk = Product("牛奶",80)
banana = Product("香蕉",40)

# 建立購物車
cart = Cart()
cart.add_item(apple)
cart.add_item(book)
cart.add_item(banana)
cart.total()

輸出:
https://ithelp.ithome.com.tw/upload/images/20251005/201647210RhW5yIi63.png

這邊來程式說明一下~

首先,第一部分:

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

說明:Product 是商品類別,每個商品都有「名稱」與「價格」兩個屬性。
創建物件時會自動呼叫 init 來初始化。

第二部分:

class Cart:
    def __init__(self):
        self.items = []  # 用 list 儲存購物車裡的所有商品
    
    def add_item(self, product):
        self.items.append(product)
        print(f"加入 {product.name} (${product.price}) 到購物車")
    
    def total(self):
        total_price = sum(item.price for item in self.items)
        print(f"購物車總金額:${total_price}")

說明:
Cart 是購物車類別,包含:
self.items:用來存放所有商品的清單
add_item():加入商品的方法
total():計算並印出購物車總金額

範例2:補習班學生資料系統

這題跟上面那題又有一點點不太一樣喔~
這題我們要來讓使用者自己輸入!!

這題的學習目標是:
1.如何建立一個可互動的系統(使用 while True 與 input)
2.如何用物件儲存資料(學生資訊)
3.物件方法應用:顯示與更新資料

一起來看看吧~(下方一樣會附上程式說明)

class Student:
    def __init__(self, name, grade, school):
        self.name = name
        self.grade = grade
        self.school = school

    def show_info(self):
        print(f"姓名:{self.name}, 年級:{self.grade}, 學校:{self.school}")

    def update_info(self, grade=None, school=None):
        if grade:
            self.grade = grade
        if school:
            self.school = school
        print(f"✅ {self.name} 的資料已更新!")

# 學生資料清單
students = []

while True:
    print("\n--- 補習班學生資料系統 ---")
    print("1. 新增學生")
    print("2. 查看所有學生")
    print("3. 查詢特定學生")
    print("4. 修改學生資料")
    print("5. 離開系統")

    choice = input("請選擇功能:")

    if choice == "1":
        name = input("輸入學生姓名:")
        grade = input("輸入學生年級:")
        school = input("輸入學生學校:")
        s = Student(name, grade, school)
        students.append(s)
        print(f"✅ 已新增學生 {name}")

    elif choice == "2":
        if not students:
            print("目前沒有學生資料。")
        else:
            print("📋 學生清單:")
            for s in students:
                s.show_info()

    elif choice == "3":
        target = input("輸入要查詢的學生姓名:")
        found = False
        for s in students:
            if s.name == target:
                s.show_info()
                found = True
                break
        if not found:
            print("❌ 查無此學生。")

    elif choice == "4":
        target = input("輸入要修改的學生姓名:")
        found = False
        for s in students:
            if s.name == target:
                new_grade = input("輸入新年級(可留空):")
                new_school = input("輸入新學校(可留空):")
                s.update_info(grade=new_grade if new_grade else None,
                              school=new_school if new_school else None)
                found = True
                break
        if not found:
            print("❌ 查無此學生。")

    elif choice == "5":
        print("📚 系統結束,再見!")
        break

    else:
        print("❌ 無效選項,請重新輸入。")

輸出:
https://ithelp.ithome.com.tw/upload/images/20251005/201647217oPTq2WtKp.png

我們先來看第一段:

class Student:
    def __init__(self, name, grade, school):
        self.name = name
        self.grade = grade
        self.school = school

說明:Student 類別代表一位學生,包含姓名、年級、學校三個屬性。

第二段:

    def show_info(self):
        print(f"姓名:{self.name}, 年級:{self.grade}, 學校:{self.school}")

    def update_info(self, grade=None, school=None):
        if grade:
            self.grade = grade
        if school:
            self.school = school
        print(f"✅ {self.name} 的資料已更新!")

說明:show_info() 用來顯示學生資料,update_info() 則用來修改年級或學校。

第三段:(主程式part)

students = []  # 儲存所有學生物件

while True:
    print("\n--- 補習班學生資料系統 ---")
    print("1. 新增學生")
    print("2. 查看所有學生")
    print("3. 查詢特定學生")
    print("4. 修改學生資料")
    print("5. 離開系統")

    choice = input("請選擇功能:")

說明:是不是看到了熟悉的while loop!
這段主要建立了一個主選單,讓使用者可以輸入選項操作系統,不同選項會對應不同功能。

第四段:(功能解釋)

1.新增學生(自行輸入資料)

name = input("輸入學生姓名:")
grade = input("輸入學生年級:")
school = input("輸入學生學校:")
s = Student(name, grade, school)
students.append(s)
print(f"✅ 已新增學生 {name}")

2.查看所有學生
列出所有 Student 物件,並呼叫 show_info()。

3.查詢特定學生
根據名字搜尋,若找到就顯示資料。

4.修改學生資料
輸入要修改的學生姓名,再更新年級或學校(可留空)。

5.離開系統
結束 while True 迴圈,跳出系統。

有沒有覺得觀念更清楚了呢?那我們今天的內容也告一段落了~

結語

今天我們把「類別」這個看似抽象的東西,
轉換成了生活化的例子:帳戶、購物車、補習班學生系統。

學到的重點是:

  • 類別幫你規劃「一群相似的東西」的規則。
  • 物件就是那些東西的「實體」。
  • __init__ 就像是「初始化設定」,確保物件出生時帶著必要的資料。
  • self 幫你把每個物件區分開,不會混在一起。

心路歷程時間~~(不想看也可略XD)

學到這邊肯定要分享我在大學時期學這邊的心!路!歷!程~
我還記得自己第一次接觸物件導向的時候,完全傻眼。

看到 class__init__self,心裡只想:我還是乖乖的寫函式就好…….

以前我都是用函式做事情,像 ATM 模擬器,每個帳戶都要自己傳餘額,
弄得我真的眼花撩亂。

可是當我真的開始寫第一個類別、建立物件、呼叫方法,看到每個帳戶、每個學生都「自己記得自己的資料」時,那一刻我才懂:

「物件不是抽象的東西,它就像程式裡的小生命,
每個物件都有自己的名字、自己的狀態,還會自己做事。」

當然,一開始我也踩過雷,搞錯 self、忘記打底線,
不會呼叫(或是亂呼叫哈哈哈)!!
但慢慢地,我開始能把生活裡的東西想成「類別」,再把它們變成物件。

回頭看,我覺得學物件導向就像把一堆亂七八糟的資料整理成小宇宙,
每個小宇宙都有自己的規則,也能互相作用。

希望你們也能像我一樣,慢慢從「霧裡看花」到「上手」,
享受物件導向帶來的方便和樂趣!!(而且看起來也比較整齊一點)

明天,我們會進一步學習:

  • 如何讓物件之間「互相串起來」 → 例如學生和課程的關聯。
  • 如何使用 繼承(inheritance) 來減少重複程式碼。

讀者也可以自己練習看看今天的範例~明天也會更上手一點!

那麼,今天辛苦啦!!我們明天見!!


上一篇
【Day20】打包你的程式碼,隨時呼叫!——函式實戰指南(下)
下一篇
【Day22】讓物件變得更聰明!物件互動與繼承的實戰演練
系列文
Python 小白的逆襲:30 天從零到能教人的精華筆記,寫給迷惘的你與當年的我自己!24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言