2025年6月11日 星期三

Python 進階概念: 物件導向程式設計 (OOP)、模組與套件、錯誤處理、文件操作、生成器 (Generators)、裝飾器 (Decorators)、上下文管理器 (Context Managers)。

 好的,在掌握了 Python 的基本語法後,接下來我們深入探討一些更進階但非常重要的概念:物件導向程式設計 (OOP)、模組與套件、錯誤處理、文件操作、生成器 (Generators)、裝飾器 (Decorators) 和上下文管理器 (Context Managers)。這些概念將幫助你寫出更結構化、可維護、高效且具備錯誤處理能力的 Python 程式碼。


Python 進階概念

1. 物件導向程式設計 (Object-Oriented Programming, OOP)

OOP 是一種程式設計範式,它將數據和處理數據的函數組織成稱為「物件」的獨立單元。主要概念包括:

  • 類別 (Class): 藍圖或模板,定義了物件的屬性 (attributes) 和行為 (methods)。
  • 物件 (Object): 類別的一個實例。
  • 封裝 (Encapsulation): 將數據(屬性)和操作數據的函數(方法)綁定在一起,並對外部隱藏其內部實現細節。
  • 繼承 (Inheritance): 允許一個類別(子類別)從另一個類別(父類別)繼承屬性和方法,實現代碼重用。
  • 多型 (Polymorphism): 允許不同的類別以自己的方式實現共同的接口或方法,表現出不同的行為。

範例: 模擬租車系統中的車輛

Python
class Vehicle:
    """
    車輛的基底類別
    """
    def __init__(self, make, model, year, daily_rate):
        # 實例屬性
        self.make = make
        self.model = model
        self.year = year
        self.daily_rate = daily_rate
        self.is_available = True

    def display_info(self):
        """顯示車輛基本資訊"""
        print(f"品牌: {self.make}, 型號: {self.model}, 年份: {self.year}")
        print(f"每日租金: ${self.daily_rate:.2f}, 可用狀態: {self.is_available}")

    def rent_vehicle(self):
        """租用車輛"""
        if self.is_available:
            self.is_available = False
            print(f"{self.make} {self.model} 已成功租用。")
            return True
        else:
            print(f"{self.make} {self.model} 目前不可用。")
            return False

    def return_vehicle(self):
        """歸還車輛"""
        if not self.is_available:
            self.is_available = True
            print(f"{self.make} {self.model} 已成功歸還。")
            return True
        else:
            print(f"{self.make} {self.model} 本來就是可用狀態。")
            return False

class Car(Vehicle): # Car 繼承自 Vehicle
    """
    汽車類別
    """
    def __init__(self, make, model, year, daily_rate, num_doors, fuel_type):
        super().__init__(make, model, year, daily_rate) # 呼叫父類別的建構子
        self.num_doors = num_doors
        self.fuel_type = fuel_type

    def display_info(self): # 多型:重寫父類別的方法
        super().display_info() # 呼叫父類別的 display_info
        print(f"門數: {self.num_doors}, 燃料類型: {self.fuel_type}")

class Motorcycle(Vehicle): # Motorcycle 繼承自 Vehicle
    """
    摩托車類別
    """
    def __init__(self, make, model, year, daily_rate, engine_size):
        super().__init__(make, model, year, daily_rate)
        self.engine_size = engine_size

    def display_info(self): # 多型:重寫父類別的方法
        super().display_info()
        print(f"引擎排氣量: {self.engine_size}cc")

# 創建物件實例
car1 = Car("Toyota", "Camry", 2022, 50.00, 4, "汽油")
bike1 = Motorcycle("Honda", "CBR500R", 2023, 30.00, 500)

print("--- 汽車資訊 ---")
car1.display_info()
car1.rent_vehicle()
car1.rent_vehicle() # 再次租用,會提示不可用
print("\n--- 摩托車資訊 ---")
bike1.display_info()
bike1.rent_vehicle()
bike1.return_vehicle()

2. 模組 (Modules) 與 套件 (Packages)

  • 模組 (Module): 一個 Python 檔案 (.py 結尾),包含 Python 程式碼(變數、函數、類別)。它允許你將程式碼組織成邏輯單元,提高可讀性和可維護性。
  • 套件 (Package): 一個包含多個模組的目錄,該目錄必須包含一個名為 __init__.py 的特殊檔案(在 Python 3.3+,這個檔案可以是空的,但在舊版本中是必需的,用來標記該目錄為 Python 套件)。套件可以包含子套件。

範例: 模組與套件的結構與使用

假設你有以下目錄結構:

my_rental_app/
├── main.py
├── vehicles/
│   ├── __init__.py
│   ├── car.py
│   └── motorcycle.py
└── utils/
    ├── __init__.py
    └── helpers.py

vehicles/car.py:

Python
# vehicles/car.py
from ..vehicles import Vehicle # 相對導入

class Car(Vehicle):
    def __init__(self, make, model, year, daily_rate, num_doors, fuel_type):
        super().__init__(make, model, year, daily_rate)
        self.num_doors = num_doors
        self.fuel_type = fuel_type

    def display_info(self):
        super().display_info()
        print(f"門數: {self.num_doors}, 燃料類型: {self.fuel_type}")

utils/helpers.py:

Python
# utils/helpers.py
def format_currency(amount, currency_code="USD"):
    """格式化金額"""
    return f"{currency_code} {amount:.2f}"

main.py:

Python
# main.py
from vehicles.car import Car         # 從套件.模組導入類別
from utils.helpers import format_currency # 從套件.模組導入函數

# 或者直接導入模組,然後使用 module.Class/function
# import vehicles.car
# import utils.helpers

my_car = Car("Tesla", "Model 3", 2024, 70.00, 4, "電動")
my_car.display_info()

price = 125.756
formatted_price = format_currency(price, "TWD")
print(f"原始價格: {price}, 格式化後: {formatted_price}")

導入方式:

  • import module_name: 導入整個模組。使用時需 module_name.function_name()
  • from module_name import function_name, ClassName: 導入模組中的特定內容。直接使用 function_name()
  • from package_name.module_name import ClassName: 從套件中的模組導入。
  • import package_name.module_name as alias: 導入並賦予別名。

3. 錯誤處理 (Error Handling)

程式在執行過程中可能會遇到錯誤(異常)。Python 使用 try-except-finally 塊來處理這些錯誤,防止程式崩潰。

  • try 放置可能拋出異常的程式碼。
  • except 捕獲並處理指定類型的異常。可以有多個 except 塊來處理不同類型的異常。
  • else (可選) 如果 try 塊中沒有發生異常,則執行 else 塊中的程式碼。
  • finally (可選) 無論 try 塊中是否發生異常,finally 塊中的程式碼都會被執行。常用於資源清理(如關閉文件或資料庫連接)。

範例:

Python
def safe_divide(numerator, denominator):
    try:
        result = numerator / denominator
    except ZeroDivisionError: # 捕獲除以零的錯誤
        print("錯誤:除數不能為零!")
        return None
    except TypeError: # 捕獲類型錯誤
        print("錯誤:輸入必須是數字!")
        return None
    except Exception as e: # 捕獲其他所有異常
        print(f"發生未知錯誤: {e}")
        return None
    else:
        # 如果 try 塊沒有異常,則執行這裡
        print("除法成功!")
        return result
    finally:
        # 無論如何都會執行
        print("除法操作完成。")

print(safe_divide(10, 2))  # 輸出: 除法成功!\n 5.0
print("-" * 20)
print(safe_divide(10, 0))  # 輸出: 錯誤:除數不能為零!\n 除法操作完成。 None
print("-" * 20)
print(safe_divide(10, "a")) # 輸出: 錯誤:輸入必須是數字!\n 除法操作完成。 None

4. 文件操作 (File Operations)

讀寫文件是常見操作。Python 提供內建函數 open() 來處理文件。

  • 模式:
    • 'r':讀取模式(預設)。
    • 'w':寫入模式(如果文件不存在則創建,存在則清空)。
    • 'a':追加模式(如果文件不存在則創建,存在則在末尾追加)。
    • 'x':獨占創建模式(如果文件已存在則報錯)。
    • 't':文本模式(預設)。
    • 'b':二進位模式。
    • '+':讀寫模式。
  • 最佳實踐:with 語句: 使用 with open(...) as f: 語句可以確保文件在使用完畢後自動關閉,即使發生錯誤也不會造成資源洩漏。

範例:

Python
file_name = "example.txt"

# 寫入文件
with open(file_name, 'w', encoding='utf-8') as f:
    f.write("這是第一行內容。\n")
    f.write("這是第二行內容,包含中文。\n")
    print(f"'{file_name}' 已寫入。")

# 讀取文件
with open(file_name, 'r', encoding='utf-8') as f:
    content = f.read() # 讀取所有內容
    print(f"\n'{file_name}' 內容:\n{content}")

# 逐行讀取文件
with open(file_name, 'r', encoding='utf-8') as f:
    print(f"\n'{file_name}' 逐行內容:")
    for line in f:
        print(line.strip()) # strip() 移除行尾的換行符

# 追加內容到文件
with open(file_name, 'a', encoding='utf-8') as f:
    f.write("這是追加的第三行。\n")
    print(f"\n'{file_name}' 已追加內容。")

# 再次讀取確認
with open(file_name, 'r', encoding='utf-8') as f:
    print(f"\n'{file_name}' 最新內容:\n{f.read()}")

5. 生成器 (Generators)

生成器是一種特殊類型的迭代器,它不會一次性在記憶體中生成所有數據,而是按需生成。這對於處理大量數據流或無限序列非常高效。

  • 關鍵字 yield 函數中包含 yield 語句就會變成一個生成器函數。每次呼叫 yield 時,函數會暫停執行並返回一個值,下次迭代時從上次暫停的地方繼續。

範例:

Python
def fibonacci_sequence(n):
    """生成前 n 個費波那契數"""
    a, b = 0, 1
    for _ in range(n):
        yield a # 每次呼叫 next() 時返回 a
        a, b = b, a + b # 更新 a 和 b

# 使用生成器
fib_gen = fibonacci_sequence(10)

print("透過 next() 逐一獲取:")
print(next(fib_gen)) # 輸出: 0
print(next(fib_gen)) # 輸出: 1
print(next(fib_gen)) # 輸出: 1

print("\n透過 for 迴圈遍歷生成器:")
for num in fibonacci_sequence(5):
    print(num)
# 輸出:
# 0
# 1
# 1
# 2
# 3

# 生成器表達式 (類似列表推導式)
squares_gen = (x * x for x in range(5))
print(f"\n生成器表達式: {squares_gen}") # 輸出: <generator object <genexpr> at ...>
print(list(squares_gen)) # 輸出: [0, 1, 4, 9, 16] (一旦轉換成列表,生成器就被耗盡)

優勢:

  • 記憶體效率: 不需要將所有數據載入記憶體。
  • 惰性求值: 數據只有在需要時才生成。
  • 處理無限序列。

6. 裝飾器 (Decorators)

裝飾器是一種在不修改原函數程式碼的情況下,動態地給函數(或類別)添加功能的語法糖。本質上,裝飾器是一個函數,它接收一個函數作為參數,並返回一個新的函數。

  • 語法: 使用 @decorator_name 放在函數定義上方。

範例: 記錄函數執行時間的裝飾器

Python
import time

def timer(func):
    """
    一個裝飾器,用於測量函數的執行時間。
    """
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs) # 呼叫原始函數
        end_time = time.time()
        print(f"函數 '{func.__name__}' 執行時間: {end_time - start_time:.4f} 秒")
        return result
    return wrapper

@timer # 這等同於 my_slow_function = timer(my_slow_function)
def my_slow_function(n):
    """一個模擬耗時操作的函數。"""
    sum_val = 0
    for i in range(n):
        sum_val += i * i
        time.sleep(0.0001) # 模擬一些耗時操作
    return sum_val

@timer
def greet(name):
    print(f"Hello, {name}!")

print(f"計算結果: {my_slow_function(1000)}")
greet("Decorator User")

優勢:

  • 代碼重用: 將通用功能(如日誌、性能測量、認證、權限檢查)應用於多個函數。
  • 代碼清晰: 減少重複程式碼,使函數的邏輯更專注於其核心職責。

7. 上下文管理器 (Context Managers)

上下文管理器允許你定義一個資源的獲取和釋放邏輯,確保資源在完成操作後被正確關閉或清理。最常見的用法是 with 語句。

  • 核心概念:
    • __enter__(self) 方法:在進入 with 塊時被呼叫,通常返回要使用的資源。
    • __exit__(self, exc_type, exc_val, exc_tb) 方法:在離開 with 塊時被呼叫,即使發生異常也會被呼叫,用於清理資源。
  • 使用 contextlib 模組的 @contextmanager 裝飾器: 更簡單地創建上下文管理器。

範例 1: 使用 with open(...) (內建上下文管理器)

Python
# 文件操作就是一個典型的上下文管理器應用
with open("test.txt", "w") as f:
    f.write("This is a test.")
# 文件會在這個 with 塊結束時自動關閉
print("文件 'test.txt' 已寫入並自動關閉。")

範例 2: 自定義上下文管理器 (模擬資料庫連接)

Python
class DatabaseConnection:
    def __init__(self, db_name):
        self.db_name = db_name
        self.connection = None

    def __enter__(self):
        print(f"連接到資料庫: {self.db_name}...")
        # 這裡應該是實際的資料庫連接邏輯
        self.connection = f"Connected to {self.db_name}" # 模擬連接對象
        return self.connection # 返回連接對象,with as variable 會接收到它

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(f"斷開與資料庫的連接: {self.db_name}...")
        # 這裡應該是實際的資料庫關閉邏輯
        self.connection = None
        if exc_type:
            print(f"在連接期間發生錯誤: {exc_val}")
        return False # 返回 False 表示如果發生異常,就讓異常繼續傳播

# 使用自定義的上下文管理器
with DatabaseConnection("my_rental_db") as db:
    print(f"操作資料庫: {db}")
    # raise ValueError("模擬一個錯誤") # 嘗試取消這行,看錯誤處理

print("資料庫連接操作完成。")

範例 3: 使用 @contextmanager (更簡潔)

Python
from contextlib import contextmanager

@contextmanager
def managed_resource(name):
    print(f"獲取資源: {name}")
    try:
        yield name # 這裡返回資源,並在此處執行 with 塊中的程式碼
    finally:
        print(f"釋放資源: {name}")

with managed_resource("租車預訂鎖") as lock:
    print(f"在鎖 {lock} 的保護下執行預訂操作...")
    # 這裡可以放置需要加鎖的關鍵代碼
print("預訂操作完成。")

優勢:

  • 資源管理: 確保資源(文件、網路連接、鎖)被正確獲取和釋放。
  • 代碼清晰: 減少錯誤處理代碼,使資源管理邏輯集中化。

這些進階概念是 Python 語言的核心,理解並熟練運用它們將極大地提升你的程式設計能力,使你能夠開發更健壯、更高效、更易於維護的應用程式,這對於未來在 AI 領域的開發也是至關重要的。

沒有留言:

張貼留言

網誌存檔