一起玩Python程式:新手寫程式也可以這麼簡單!

Week 14: 基礎物件入門 - 打造程式生物圖鑑!

張傑帆

National Taiwan University

基礎物件入門 - 打造程式生物圖鑑!

目錄

Alt1

🎯 學習目標

今天我們要創造屬於自己的神奇生物世界!

本週你將學會:

  • 🏗️ 理解類別(Class)與物件(Object)的概念
  • 🎨 設計具有屬性和方法的類別
  • 🐉 創建寶可夢角色並賦予牠們獨特的能力
  • ⚔️ 實作寶可夢對戰系統
  • 📖 建立生物圖鑑收集系統

準備好創造你的神奇寶貝世界了嗎?

Let's create! 🐍✨

🌟 物件導向是什麼?

想像一下: 你想製作一款遊戲,裡面有很多不同的寶可夢...

🤔 問題來了:

  • 如果要創造 100 隻寶可夢,每隻都要寫一次名字、等級、HP嗎?
  • 如果每隻寶可夢都會攻擊,要寫 100 次攻擊功能嗎?

💡 物件導向的解決方案:
創造一個「寶可夢設計圖」(類別),然後根據這個設計圖製造出許多寶可夢(物件)!

🏗️ 類別 vs 物件

概念 比喻 程式中的角色
類別 (Class) 🏗️ 建築設計圖 定義物件的模板
物件 (Object) 🏠 實際的房子 根據類別建立的實例

舉例說明:

  • 🏗️ 類別:「寶可夢」的設計圖(有名字、等級、HP、會攻擊)
  • 🏠 物件:皮卡丘(名字=皮卡丘, 等級=5, HP=35, 會十萬伏特)
  • 🏠 物件:傑尼龜(名字=傑尼龜, 等級=5, HP=44, 會水槍)

📦 定義第一個類別

讓我們創造最簡單的寶可夢類別!

# filepath: example_01_basic_class.py
class Pokemon:
    """這是寶可夢的基礎類別"""
    pass  # 先建立一個空的類別

# 創建第一隻寶可夢物件
pikachu = Pokemon()
squirtle = Pokemon()

print(type(pikachu))  # <class '__main__.Pokemon'>
print(pikachu)        # <__main__.Pokemon object at 0x...>

🎯 關鍵概念:

  • class 關鍵字用來定義類別
  • 類別名稱通常使用大寫開頭(PascalCase)
  • Pokemon() 創建類別的實例(物件)

🎨 給寶可夢加上屬性

現在讓我們的寶可夢有自己的特徵!

# filepath: example_02_attributes.py
class Pokemon:
    def __init__(self, name, level, hp):
        """初始化寶可夢的屬性"""
        self.name = name      # 名字
        self.level = level    # 等級
        self.hp = hp          # 生命值
        self.max_hp = hp      # 最大生命值

# 創建具有屬性的寶可夢
pikachu = Pokemon("皮卡丘", 5, 35)
squirtle = Pokemon("傑尼龜", 5, 44)

print(f"{pikachu.name} 等級:{pikachu.level} HP:{pikachu.hp}")
print(f"{squirtle.name} 等級:{squirtle.level} HP:{squirtle.hp}")

🔍 init 方法解析

# filepath: example_03_init_explained.py
class Pokemon:
    def __init__(self, name, level, hp):
        # self 代表「這個物件本身」
        self.name = name    # 物件的名字屬性
        self.level = level  # 物件的等級屬性
        self.hp = hp        # 物件的生命值屬性

重要觀念:

  • __init__ 是「初始化方法」,創建物件時自動執行
  • self 代表物件本身(就像人稱代名詞「我」)
  • self.name 表示「這個物件的名字」
  • 參數 name, level, hp 是創建物件時需要提供的資料

🎮 給寶可夢加上技能(方法)

# filepath: example_04_methods.py
class Pokemon:
    def __init__(self, name, level, hp):
        self.name = name
        self.level = level
        self.hp = hp
        self.max_hp = hp
    
    def show_status(self):
        """顯示寶可夢的狀態"""
        print(f"🎯 {self.name}")
        print(f"   等級:Lv.{self.level}")
        print(f"   HP:{self.hp}/{self.max_hp}")
    
    def attack(self, target):
        """攻擊另一隻寶可夢"""
        damage = self.level * 5
        target.hp -= damage
        print(f"💥 {self.name} 攻擊 {target.name},造成 {damage} 點傷害!")

🎯 完整的寶可夢類別範例

# filepath: example_05_complete_pokemon.py
class Pokemon:
    def __init__(self, name, type, level, hp, attack_power):
        self.name = name
        self.type = type            # 屬性(電、水、火等)
        self.level = level
        self.hp = hp
        self.max_hp = hp
        self.attack_power = attack_power
    
    def show_status(self):
        """顯示狀態"""
        hp_bar = "❤" * (self.hp // 10) + "🖤" * ((self.max_hp - self.hp) // 10)
        print(f"\n{'='*40}")
        print(f"🎯 名字:{self.name} ({self.type}系)")
        print(f"   等級:Lv.{self.level}")
        print(f"   HP:{hp_bar} {self.hp}/{self.max_hp}")
        print(f"   攻擊力:{self.attack_power}")
        print(f"{'='*40}")

(接上頁)

# filepath: example_05_complete_pokemon.py (continued)
    def attack(self, target):
        """攻擊目標"""
        if self.hp <= 0:
            print(f"❌ {self.name} 已經無法戰鬥了!")
            return
        
        damage = self.attack_power + self.level * 2
        target.hp = max(0, target.hp - damage)
        
        print(f"\n⚔ {self.name} 使用攻擊!")
        print(f"💥{target.name} 造成 {damage} 點傷害!")
        
        if target.hp <= 0:
            print(f"💀 {target.name} 失去戰鬥能力了!")
    
    def heal(self, amount):
        """回復生命值"""
        old_hp = self.hp
        self.hp = min(self.max_hp, self.hp + amount)
        healed = self.hp - old_hp
        print(f"💚 {self.name} 回復了 {healed} 點 HP!")

🎮 使用完整的寶可夢類別

# filepath: example_06_using_pokemon.py
# 創建寶可夢
pikachu = Pokemon("皮卡丘", "電", 10, 60, 25)
squirtle = Pokemon("傑尼龜", "水", 8, 70, 20)
charmander = Pokemon("小火龍", "火", 9, 65, 22)

# 顯示初始狀態
pikachu.show_status()
squirtle.show_status()

# 戰鬥場景
print("\n🎮 對戰開始!")
pikachu.attack(squirtle)
squirtle.show_status()

squirtle.attack(pikachu)
pikachu.show_status()

# 使用回復
pikachu.heal(15)
pikachu.show_status()

小練習 1:創建你的第一隻寶可夢

🎯 任務: 創建一個專屬於你的寶可夢類別!

  1. 定義一個 MyPokemon 類別

  2. 包含屬性:名字、屬性類型、等級、HP、攻擊力

  3. 實作方法:

    • show_info(): 顯示寶可夢的完整資訊
    • level_up(): 提升等級(等級+1,HP+10,攻擊力+3)
    • take_damage(damage): 受到傷害
  4. 創建至少 2 隻不同的寶可夢

  5. 測試所有方法的功能

💡 小練習 1 提示

# filepath: exercise_01_hint.py
class MyPokemon:
    def __init__(self, name, type, level, hp, attack):
        # TODO: 初始化屬性
        pass
    
    def show_info(self):
        # TODO: 顯示寶可夢資訊
        pass
    
    def level_up(self):
        # TODO: 提升等級和能力值
        pass
    
    def take_damage(self, damage):
        # TODO: 計算受到的傷害
        pass

# 測試你的類別
# my_pokemon = MyPokemon("???", "???", 1, 50, 15)

休息時間 ☕

讓我們稍作休息,準備迎接更精彩的內容!

思考問題:

  • 你最喜歡哪種類型的寶可夢?
  • 如果要設計一個「進化系統」,你會怎麼做?
  • 寶可夢之間的「屬性相剋」該如何實作?

🎨 物件的屬性與方法深入探討

讓我們的寶可夢更加強大!

屬性的類型:

  1. 實例屬性:每個物件獨有的(如:名字、HP)
  2. 類別屬性:所有物件共用的(如:物種特性)

方法的類型:

  1. 實例方法:操作單一物件(如:攻擊、回復)
  2. 類別方法:操作整個類別(如:統計所有寶可夢數量)
  3. 靜態方法:工具函式(如:計算屬性相剋傷害)

🏆 進階寶可夢類別設計

# filepath: example_07_advanced_pokemon.py
class AdvancedPokemon:
    # 類別屬性:記錄所有創建的寶可夢數量
    total_count = 0
    
    # 類別屬性:屬性相剋表
    type_chart = {
        "火": {"水": 0.5, "草": 2.0, "火": 0.5},
        "水": {"火": 2.0, "草": 0.5, "水": 0.5},
        "草": {"水": 2.0, "火": 0.5, "草": 0.5},
        "電": {"水": 2.0, "草": 0.5, "飛行": 2.0},
    }
    
    def __init__(self, name, poke_type, level=5):
        self.name = name
        self.type = poke_type
        self.level = level
        self.hp = 50 + level * 5
        self.max_hp = self.hp
        self.attack_power = 20 + level * 2
        self.exp = 0
        self.exp_to_next_level = 100
        
        AdvancedPokemon.total_count += 1

(接上頁)

# filepath: example_07_advanced_pokemon.py (continued)
    def attack(self, target):
        """考慮屬性相剋的攻擊"""
        if self.hp <= 0:
            print(f"❌ {self.name} 已經無法戰鬥了!")
            return
        
        # 計算基礎傷害
        base_damage = self.attack_power + self.level * 2
        
        # 計算屬性相剋加成
        multiplier = self.get_type_multiplier(self.type, target.type)
        damage = int(base_damage * multiplier)
        
        target.hp = max(0, target.hp - damage)
        
        # 顯示效果
        if multiplier > 1:
            print(f"⚡ 效果絕佳!")
        elif multiplier < 1:
            print(f"🛡 效果不佳...")
        
        print(f"💥 {self.name}{target.name} 造成 {damage} 點傷害!")
        
        # 獲得經驗值
        if target.hp <= 0:
            self.gain_exp(50)

(接上頁)

# filepath: example_07_advanced_pokemon.py (continued)
    @staticmethod
    def get_type_multiplier(attacker_type, defender_type):
        """靜態方法:計算屬性相剋倍率"""
        if attacker_type in AdvancedPokemon.type_chart:
            if defender_type in AdvancedPokemon.type_chart[attacker_type]:
                return AdvancedPokemon.type_chart[attacker_type][defender_type]
        return 1.0
    
    def gain_exp(self, amount):
        """獲得經驗值"""
        self.exp += amount
        print(f"⭐ {self.name} 獲得了 {amount} 點經驗值!")
        
        # 檢查是否升級
        while self.exp >= self.exp_to_next_level:
            self.level_up()
    
    def level_up(self):
        """升級"""
        self.exp -= self.exp_to_next_level
        self.level += 1
        self.max_hp += 10
        self.hp = self.max_hp
        self.attack_power += 5
        self.exp_to_next_level = int(self.exp_to_next_level * 1.2)
        
        print(f"🎊 恭喜!{self.name} 升級到 Lv.{self.level}!")

(接上頁)

# filepath: example_07_advanced_pokemon.py (continued)
    @classmethod
    def get_total_count(cls):
        """類別方法:獲取創建的寶可夢總數"""
        return cls.total_count
    
    def __str__(self):
        """字串表示法"""
        return f"{self.name}(Lv.{self.level}, HP:{self.hp}/{self.max_hp})"
    
    def __repr__(self):
        """開發者友善的表示法"""
        return f"AdvancedPokemon('{self.name}', '{self.type}', {self.level})"

⚔️ 實作對戰功能

讓我們創建一個完整的對戰系統!

# filepath: example_08_battle_system.py
class BattleSystem:
    """寶可夢對戰系統"""
    
    def __init__(self, pokemon1, pokemon2):
        self.pokemon1 = pokemon1
        self.pokemon2 = pokemon2
        self.turn = 1
    
    def start_battle(self):
        """開始對戰"""
        print(f"\n{'='*50}")
        print(f"🎮 對戰開始!")
        print(f"   {self.pokemon1.name} vs {self.pokemon2.name}")
        print(f"{'='*50}\n")
        
        while self.pokemon1.hp > 0 and self.pokemon2.hp > 0:
            self.play_turn()
        
        self.announce_winner()

(接上頁)

# filepath: example_08_battle_system.py (continued)
    def play_turn(self):
        """執行一個回合"""
        print(f"\n--- 第 {self.turn} 回合 ---")
        
        # 寶可夢1攻擊
        if self.pokemon1.hp > 0:
            self.pokemon1.attack(self.pokemon2)
        
        # 寶可夢2反擊
        if self.pokemon2.hp > 0:
            self.pokemon2.attack(self.pokemon1)
        
        # 顯示雙方狀態
        print(f"\n📊 當前狀態:")
        print(f"   {self.pokemon1}")
        print(f"   {self.pokemon2}")
        
        self.turn += 1
        
        # 暫停一下讓玩家看清楚
        input("\n按 Enter 繼續...")
    
    def announce_winner(self):
        """宣布獲勝者"""
        print(f"\n{'='*50}")
        if self.pokemon1.hp > 0:
            print(f"🏆 {self.pokemon1.name} 獲勝!")
        else:
            print(f"🏆 {self.pokemon2.name} 獲勝!")
        print(f"{'='*50}")

📖 建立寶可夢圖鑑系統

# filepath: example_09_pokedex.py
class Pokedex:
    """寶可夢圖鑑系統"""
    
    def __init__(self):
        self.pokemons = []  # 收集到的寶可夢列表
    
    def add_pokemon(self, pokemon):
        """新增寶可夢到圖鑑"""
        self.pokemons.append(pokemon)
        print(f"✅ {pokemon.name} 已加入圖鑑!")
        print(f"📚 目前收集了 {len(self.pokemons)} 隻寶可夢")
    
    def show_all(self):
        """顯示所有收集的寶可夢"""
        if not self.pokemons:
            print("📭 圖鑑是空的,快去收集寶可夢吧!")
            return
        
        print(f"\n{'='*60}")
        print(f"📖 寶可夢圖鑑(共 {len(self.pokemons)} 隻)")
        print(f"{'='*60}")
        
        for i, pokemon in enumerate(self.pokemons, 1):
            print(f"{i:3d}. {pokemon}")

(接上頁)

# filepath: example_09_pokedex.py (continued)
    def search_by_name(self, name):
        """根據名字搜尋寶可夢"""
        results = [p for p in self.pokemons if name in p.name]
        
        if results:
            print(f"\n🔍 找到 {len(results)} 隻寶可夢:")
            for pokemon in results:
                print(f"   • {pokemon}")
        else:
            print(f"❌ 找不到名字包含 '{name}' 的寶可夢")
    
    def search_by_type(self, poke_type):
        """根據屬性搜尋寶可夢"""
        results = [p for p in self.pokemons if p.type == poke_type]
        
        if results:
            print(f"\n🔍 找到 {len(results)}{poke_type}系 寶可夢:")
            for pokemon in results:
                print(f"   • {pokemon}")
        else:
            print(f"❌ 找不到 {poke_type}系 的寶可夢")
    
    def get_statistics(self):
        """顯示統計資訊"""
        if not self.pokemons:
            return
        
        total = len(self.pokemons)
        avg_level = sum(p.level for p in self.pokemons) / total
        max_level_pokemon = max(self.pokemons, key=lambda p: p.level)
        
        print(f"\n📊 圖鑑統計:")
        print(f"   總數量:{total} 隻")
        print(f"   平均等級:{avg_level:.1f}")
        print(f"   最高等級:{max_level_pokemon.name} (Lv.{max_level_pokemon.level})")

🎯 完整應用範例

# filepath: example_10_complete_demo.py
# 創建寶可夢
pikachu = AdvancedPokemon("皮卡丘", "電", 10)
charizard = AdvancedPokemon("噴火龍", "火", 12)
blastoise = AdvancedPokemon("水箭龜", "水", 11)
venusaur = AdvancedPokemon("妙蛙花", "草", 11)

# 創建圖鑑
pokedex = Pokedex()
pokedex.add_pokemon(pikachu)
pokedex.add_pokemon(charizard)
pokedex.add_pokemon(blastoise)
pokedex.add_pokemon(venusaur)

# 顯示圖鑑
pokedex.show_all()

# 搜尋功能
pokedex.search_by_type("火")
pokedex.search_by_name("龍")

# 統計資訊
pokedex.get_statistics()
# filepath: example_10_complete_demo.py (continued)
# 顯示總數
print(f"\n🌟 目前共創建了 {AdvancedPokemon.get_total_count()} 隻寶可夢")

# 開始對戰
print("\n" + "="*60)
print("準備對戰!")
battle = BattleSystem(pikachu, blastoise)
battle.start_battle()

# 顯示對戰後的狀態
print("\n對戰結束後的圖鑑狀態:")
pokedex.show_all()
pokedex.get_statistics()

小練習 2:寶可夢對戰系統

🎯 任務: 創建一個完整的寶可夢對戰遊戲!

📋 要求:

  1. 創建至少 3 隻不同屬性的寶可夢
  2. 實作以下功能:
    • 回合制對戰系統
    • 技能選擇(至少 2 種攻擊技能)
    • 道具使用(如:藥水回復 HP)
    • 屬性相剋計算
  3. 創建一個簡單的圖鑑系統記錄對戰結果
  4. 加入隨機事件(如:暴擊、閃避)

💡 小練習 2 提示

# filepath: exercise_02_hint.py
import random

class BattlePokemon:
    def __init__(self, name, poke_type, level):
        # TODO: 初始化屬性
        self.skills = []  # 技能列表
        pass
    
    def add_skill(self, skill_name, damage, skill_type):
        """新增技能"""
        # TODO: 將技能加入列表
        pass
    
    def use_skill(self, skill_index, target):
        """使用技能攻擊"""
        # TODO: 執行技能並計算傷害
        # 考慮屬性相剋和隨機暴擊
        pass
    
    def use_item(self, item_type):
        """使用道具"""
        # TODO: 實作藥水等道具效果
        pass

🧠 運算思維總整理

今天我們學到的運算思維技巧:

1️⃣ 抽象化 (Abstraction)

概念: 將現實世界的事物抽象為程式中的類別和物件

例子:

  • 真實的寶可夢 → 程式中的 Pokemon 類別
  • 寶可夢的特徵(名字、等級、HP) → 類別的屬性
  • 寶可夢的行為(攻擊、回復) → 類別的方法

重點: 找出事物的共同特徵,忽略不重要的細節

2️⃣ 問題分解 (Decomposition)

概念: 將複雜的系統分解為多個簡單的部分

例子:

寶可夢對戰系統
├── Pokemon 類別(基礎寶可夢)
├── BattleSystem 類別(對戰邏輯)
├── Pokedex 類別(圖鑑管理)
└── 各種方法(攻擊、回復、升級等)

重點: 每個類別負責一個明確的功能,不要混在一起

3️⃣ 模式識別 (Pattern Recognition)

概念: 識別物件間的共同模式和規律

例子:

  • 所有寶可夢都有:名字、等級、HP、攻擊力
  • 所有寶可夢都會:攻擊、回復、升級
  • 屬性相剋遵循固定的規則(火剋草,水剋火,草剋水)

重點: 找出共同模式,避免重複撰寫相同的程式碼

🔑 物件導向的三大核心概念

概念 說明 實際應用
封裝 (Encapsulation) 將資料和操作資料的方法包在一起 Pokemon 類別包含屬性和方法
繼承 (Inheritance) 從現有類別創建新類別(下週學習) 火系寶可夢、水系寶可夢繼承自基礎寶可夢
多型 (Polymorphism) 不同物件對相同訊息有不同反應(下週學習) 不同寶可夢的攻擊技能效果不同

本週重點: 封裝(將相關的資料和功能組織在一起)

本週總結 🎉

✅ 你今天學會了:

  1. 類別與物件的基礎概念

    • 類別是設計圖,物件是實例
    • 使用 class 定義類別,使用 ClassName() 創建物件
  2. 屬性與方法的設計

    • __init__ 初始化方法設定物件屬性
    • self 代表物件本身
    • 實例方法、類別方法、靜態方法的差異
  3. 實際應用

    • 創建寶可夢角色系統
    • 實作對戰功能
    • 建立圖鑑收集系統

🎯 重要觀念回顧

# filepath: summary_concepts.py
# 類別定義
class Pokemon:
    # 類別屬性(所有物件共用)
    species = "寶可夢"
    
    # 初始化方法
    def __init__(self, name):
        # 實例屬性(每個物件獨有)
        self.name = name
    
    # 實例方法
    def greet(self):
        print(f"我是 {self.name}!")
    
    # 類別方法
    @classmethod
    def get_species(cls):
        return cls.species
    
    # 靜態方法
    @staticmethod
    def calculate_damage(attack, defense):
        return max(1, attack - defense)

# 使用類別
pikachu = Pokemon("皮卡丘")
pikachu.greet()

作業:創造你的寶可夢世界 🌟

選擇一個適合你的挑戰等級!

📝 選項一:基礎練習 (適合初學者)

任務:創建寶可夢收集系統

  1. 創建一個 SimplePokemon 類別,包含:

    • 屬性:名字、類型、等級、HP
    • 方法:顯示資訊、升級、回復HP
  2. 創建一個 PokemonTrainer 類別(訓練師),包含:

    • 屬性:訓練師名字(加)、寶可夢列表
    • 方法:收服寶可夢、顯示所有寶可夢、釋放寶可夢(加)
  3. 創建至少 5 隻寶可夢並加入訓練師的隊伍

🔧 選項二:進階應用 (有點挑戰性)

任務:完整的寶可夢對戰系統

  1. 實作完整的 Pokemon 類別,包含:

    • 多種屬性(火、水、草、電等)
    • 屬性相剋計算
    • 經驗值和升級系統
    • 至少 3 種不同的攻擊技能
  2. 實作 Battle 類別,包含:

    • 回合制對戰
    • 技能選擇介面
    • 道具使用(藥水、能量飲料等)
    • 對戰記錄和統計
  3. 實作 Pokedex 類別,包含:

    • 自動記錄遇到的寶可夢
    • 按屬性、等級搜尋
    • 顯示完整度百分比

🚀 選項三:創意發揮 (自由度高)

任務:打造你的夢幻寶可夢遊戲

必要功能:

  1. 至少 3 個以上的類別(自行設計)
  2. 實作繼承關係(進化系統、專屬寶可夢等)
  3. 完整的遊戲循環和選單系統

創意方向建議:

  • 🏆 寶可夢競技場(多人對戰)
  • 🗺️ 冒險系統(地圖探索、隨機遭遇)
  • 🏪 商店系統(購買道具、強化寶可夢)
  • 🎮 小遊戲(猜數字抓寶可夢、訓練小遊戲)
  • 💾 存檔系統(使用檔案讀寫)

額外可以做的

  • 使用圖形介面 (tkinter)
  • 美化輸出(顏色、動畫效果)
  • 資料持久化(儲存/讀取遊戲)
  • 完整的使用文檔

"物件導向不只是程式技術,更是一種思考方式!" 💭

下週見!👋

-----------------------------------------

圖片放大特效

table樣式

讓寶可夢可以做事情!

**📋 要求:**

⏰ **時間:15 分鐘**

⏰ **時間:20 分鐘**

**評分標準:** - 類別定義正確 (40%) - 方法功能完整 (40%) - 程式可執行 (20%)

--- **評分標準:** - 類別設計合理 (30%) - 功能完整性 (40%) - 程式碼品質(註解、命名) (20%) - 創意加分 (10%)

**評分標準:** - 創意與完整度 (40%) - 程式架構設計 (30%) - 功能實用性 (20%) - 額外亮點 (10%) **加分項目:**

--- ## 📚 繳交方式 **截止日期:** 下週上課前 **繳交內容:** 1. Python 程式檔案(.py) 2. 執行結果截圖或文字輸出 3. 簡短說明文件(200 字以內,說明你的設計思路) **繳交方式:** 上傳至 Google Classroom --- ## 💡 學習資源 **推薦閱讀:** - Python 官方文件:Classes - Real Python: Object-Oriented Programming in Python - 線上遊戲:CodeCombat(練習物件導向概念) **下週預告:** - 🎯 物件導向進階:繼承與多型 - 🎨 創建寶可夢進化系統 - 🏆 設計寶可夢道館挑戰賽 準備好繼續你的寶可夢大師之旅了嗎? 🚀

**記住:**

!height:250px ### Q&A 時間 🙋 有任何問題嗎?歡迎提問! **常見問題:** - 什麼時候該用類別?什麼時候用函式就好? - `self` 到底是什麼?為什麼一定要寫? - 類別方法和靜態方法有什麼差別? **課後聯絡:** 📧 Email 或 Discord Made changes. ---