物件導向程式設計是 Python 建構複雜應用程式的根本,本文將解析類別、物件、繼承、多型和封裝等核心概念。首先,我們會說明如何定義類別和建立物件,並透過實際案例展示如何使用建構子初始化物件屬性。接著,將探討繼承機制,說明如何從父類別繼承屬性和方法,以及如何覆寫父類別的方法來實作多型。最後,將會講解封裝的重要性,以及如何使用存取修飾子(例如雙底線)來控制物件屬性的存取許可權,以提升程式碼的安全性與可維護性。

物件導向程式設計:Python 實作

物件導向程式設計(Object-Oriented Programming, OOP)是 Python 中一種重要的程式設計正規化,用於建立模組化和可重複使用的程式碼。本章節將探討類別和物件的核心概念,以及如何定義屬性和方法來封裝功能。同時,也會介紹 OOP 的基本原則,如繼承、多型、封裝和存取修飾符的使用。讀者將學習建構子、解構子和運算元多載等進階主題,為建立複雜的應用程式奠定堅實的基礎。

類別與物件

在 Python 中,類別和物件的概念是物件導向程式設計的基礎。類別作為物件的藍圖,封裝了物件的資料和方法,以操控這些資料。瞭解這些基本概念對於撰寫高效、模組化的程式碼至關重要。

Python 中的類別使用 class 關鍵字定義。類別的定義包括類別名稱和一個程式碼區塊,用於指定類別的屬性和方法。例如,定義一個幾何形狀的類別,如 Circle,可以包含半徑屬性,以及計算面積和周長的方法。

class Circle:
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14159 * self.radius * self.radius

    def circumference(self):
        return 2 * 3.14159 * self.radius

內容解密:

  1. __init__ 方法:這是一個建構子,在建立類別的例項時自動呼叫,用於初始化 Circle 例項的半徑屬性。
  2. areacircumference 方法:這些方法定義了與 Circle 例項相關的操作,分別用於計算圓的面積和周長。
  3. 屬性的存取:使用點(.)符號來存取物件的屬性。

建立 Circle 物件的例項如下:

# 建立 Circle 物件
my_circle = Circle(5)

# 存取物件的屬性和方法
print("半徑:", my_circle.radius)
print("面積:", my_circle.area())
print("周長:", my_circle.circumference())

輸出結果:

半徑: 5
面積: 78.53975
周長: 31.4159

在這個例子中,my_circleCircle 類別的一個例項,其半徑被初始化為 5。呼叫 area()circumference() 方法可以計算出這個圓的特定屬性。

封裝與類別屬性

封裝是類別設計的一個重要方面,它涉及將資料(屬性)和程式碼(方法)繫結在一起。這樣可以隱藏物件的內部表示,只向外部提供必要的介面,從而使得內部實作的變更不會影響到外部的互動。

Python 中的屬性可以使用點(.)符號存取。按照慣例,私有屬性會在前面加上雙底線,但這主要是為了防止非預期的存取,因為 Python 使用名稱改寫(name mangling)而不是嚴格的私有存取控制。

考慮擴充套件 Circle 類別以維護已建立圓形的數量,可以使用類別屬性來實作:

class Circle:
    circle_count = 0  # 類別屬性

    def __init__(self, radius):
        self.radius = radius
        Circle.circle_count += 1  # 每當建立新圓形時遞增計數

    def area(self):
        return 3.14159 * self.radius * self.radius

    def circumference(self):
        return 2 * 3.14159 * self.radius

# 示範類別屬性
first_circle = Circle(7)
second_circle = Circle(14)
print("總共建立的圓形數量:", Circle.circle_count)

輸出結果:

總共建立的圓形數量: 2

在這個例子中,first_circlesecond_circle 是不同的物件,每個物件都有自己的半徑屬性。然而,它們分享對類別屬性 circle_count 的存取權,這個屬性用於追蹤已建立的 Circle 例項數量。這展示了類別屬性(在所有例項之間分享)和例項屬性(特定於每個例項)之間的重要區別。

方法與繼承

類別中的方法可以操縱物件內的資料,提供封裝的功能。這些方法通常操作於類別的例項上,也可以修改例項的狀態。

額外的方法可以顯著增強類別的功能。例如,檢查一個圓是否完全包圍另一個圓的方法:

class Circle:
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14159 * self.radius * self.radius

    def circumference(self):
        return 2 * 3.14159 * self.radius

    def contains(self, other_circle):
        return self.radius >= other_circle.radius

a_circle = Circle(10)
b_circle = Circle(5)
print("a_circle 是否包含 b_circle?", a_circle.contains(b_circle))

輸出結果:

a_circle 是否包含 b_circle? True

contains 方法使得兩個圓形物件之間的比較成為可能,根據一個圓的半徑是否至少與另一個圓一樣大來傳回布林結果。這展示了方法如何透過定義與物件相關的操作來擴充套件類別的功能。

此外,類別可以使用繼承機制,即子類別從父類別繼承屬性和方法。這促進了程式碼的重用和相關類別的邏輯結構。例如,透過引入一個繼承自 CircleCylinder 類別,可以計算額外的屬性。

繼承範例:

class Cylinder(Circle):
    def __init__(self, radius, height):
        super().__init__(radius)
        self.height = height

    def volume(self):
        return self.area() * self.height

cylinder = Cylinder(5, 10)
print("圓柱體積:", cylinder.volume())

內容解密:

  1. Cylinder 繼承自 Circle:透過繼承,Cylinder 可以重用 Circle 的屬性和方法。
  2. super().__init__(radius):呼叫父類別的建構子來初始化半徑。
  3. volume 方法:計算圓柱體的體積,利用了從 Circle 繼承而來的 area 方法。

本章節介紹了 Python 中物件導向程式設計的基本概念,包括類別、物件、封裝、繼承等。透過這些技術,可以建立模組化、可維護且可擴充套件的程式碼,為開發複雜應用系統奠定基礎。接下來的章節將進一步探討更多進階主題。

物件導向程式設計:類別與屬性、方法

在 Python 中,類別(Class)是物件導向程式設計(OOP)的基礎。類別定義了物件的屬性和行為,屬性代表物件的狀態,而方法則定義了物件的行為。

繼承與多型

繼承是物件導向程式設計中的一個重要概念,允許我們建立一個新的類別,繼承現有類別的屬性和方法。例如,我們可以建立一個 Cylinder 類別,繼承自 Circle 類別,並新增 volumesurface_area 方法。

class Cylinder(Circle):
    def __init__(self, radius, height):
        super().__init__(radius)
        self.height = height

    def volume(self):
        return self.area() * self.height

    def surface_area(self):
        circle_area = self.area()
        side_area = self.circumference() * self.height
        return 2 * circle_area + side_area

cylinder = Cylinder(3, 5)
print("圓柱體積:", cylinder.volume())
print("圓柱表面積:", cylinder.surface_area())

內容解密:

  1. Cylinder 類別繼承自 Circle 類別,使用 super().__init__(radius) 初始化父類別的屬性。
  2. volume 方法計算圓柱體的體積,使用 self.area() 取得圓面積。
  3. surface_area 方法計算圓柱體的表面積,包括兩個圓面積和側面積。

屬性與方法

屬性是類別中的變數,代表物件的狀態。方法則是類別中的函式,定義了物件的行為。

例項屬性與類別屬性

例項屬性是每個物件獨有的,而類別屬性則是所有物件共用的。

class Car:
    num_of_wheels = 4  # 類別屬性

    def __init__(self, make, model, year):
        self.make = make  # 例項屬性
        self.model = model
        self.year = year

car1 = Car("Toyota", "Camry", 2020)
car2 = Car("Honda", "Accord", 2019)

print("Car1 製造商:", car1.make)
print("Car2 型號:", car2.model)
print("Car1 車輪數:", car1.num_of_wheels)
print("Car2 車輪數:", car2.num_of_wheels)

內容解密:

  1. num_of_wheels 是類別屬性,所有 Car 物件共用。
  2. makemodelyear 是例項屬性,每個 Car 物件獨有。

方法型別

Python 中的方法有三種型別:例項方法、類別方法和靜態方法。

例項方法

例項方法是操作例項屬性的函式。

class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year

    def start_engine(self):
        return f"{self.make} {self.model} 的引擎啟動了。"

car_instance = Car("Tesla", "Model S", 2022)
print(car_instance.start_engine())

內容解密:

  1. start_engine 是例項方法,使用 self.makeself.model 例項屬性。

類別方法

類別方法是操作類別屬性的函式,使用 @classmethod 裝飾器。

class Car:
    num_of_wheels = 4

    @classmethod
    def change_wheel_count(cls, count):
        cls.num_of_wheels = count

Car.change_wheel_count(3)
print("更新後的車輪數:", Car.num_of_wheels)

內容解密:

  1. change_wheel_count 是類別方法,使用 cls.num_of_wheels 修改類別屬性。

靜態方法

靜態方法是與類別或例項無關的函式,使用 @staticmethod 裝飾器。

class Car:
    @staticmethod
    def car_category(speed):
        if speed > 200:
            return "跑車"
        elif speed > 120:
            return "轎車"
        else:
            return "小型車"

category = Car.car_category(150)
print("速度 150 的汽車類別:", category)

內容解密:

  1. car_category 是靜態方法,不使用任何類別或例項屬性。

封裝與存取修飾子:物件導向程式設計的核心原則

在物件導向程式設計中,封裝是一項基本原則,旨在限制對物件內部元件的直接存取,以防止資料被意外修改。封裝的概念不僅僅是將資料包含在物件內,還包括提供受控的方法來操作這些資料。

封裝的概念

封裝的核心思想是將資料(屬性)和操作這些資料的方法(函式)繫結在一個單元或類別中。這樣建立了一個自包含的環境,保護了物件的內部狀態不被未授權存取,從而維護了資料的完整性並最小化了副作用。

讓我們考慮一個簡單的 Python 示例:

class Account:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.__balance = balance  # 私有屬性

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            return f"已將 {amount} 新增至餘額"
        return "無效的存款金額"

    def withdraw(self, amount):
        if amount <= self.__balance:
            self.__balance -= amount
            return f"已從餘額中提取 {amount}"
        return "餘額不足"

    def get_balance(self):
        return self.__balance

內容解密:

  • __init__ 方法初始化帳戶擁有者和餘額,其中 __balance 被標記為私有屬性。
  • depositwithdraw 方法提供了受控的介面來修改餘額。
  • get_balance 方法允許檢索當前餘額。

在此範例中,Account 類別封裝了每個帳戶擁有者的餘額,透過其 deposit()withdraw() 方法提供受控的互動。__balance 屬性透過命名慣例被標記為私有,只能在類別內部存取,從而透過隱藏餘額的內部表示來強制執行封裝。

Python 中的存取修飾子

Python 使用命名慣例來表示預期的存取層級,而不是像其他程式語言那樣使用內建的存取修飾子(public、protected、private)。

  • 公有屬性:預設情況下,屬性可以在任何地方存取,沒有特殊的語法。
  • 受保護屬性:透過單一下劃線字首(例如 _balance)表示,表明該屬性主要用於內部或子類別。
  • 私有屬性:透過雙下劃線字首(例如 __balance)表示,這會觸發名稱改寫,使屬性更難從類別外部存取。

雖然 Python 的存取修飾子根據命名慣例,但它們在遵守封裝原則方面發揮著關鍵作用。讓我們透過程式碼範例來探討這些修飾子:

class Parent:
    def __init__(self):
        self.public_var = "我是一個公有變數"
        self._protected_var = "我是一個受保護的變數"
        self.__private_var = "我是一個私有變數"

    def access_methods(self):
        return (self.public_var, self._protected_var, self.__private_var)

class Child(Parent):
    def access_parent_vars(self):
        return (self.public_var, self._protected_var)

def test_access():
    parent_obj = Parent()
    print(f"公有: {parent_obj.public_var}")
    print(f"受保護: {parent_obj._protected_var}")
    # 使用名稱改寫存取私有變數
    print(f"私有: {parent_obj._Parent__private_var}")

test_access()

內容解密:

  • Parent 類別展示了不同存取層級的屬性定義。
  • Child 類別嘗試存取父類別的屬性,展示了私有屬性的不可存取性。
  • test_access 函式演示瞭如何存取不同層級的屬性,包括使用名稱改寫存取私有屬性。

使用封裝的設計考量

  1. 介面清晰度:封裝需要類別與其使用者之間具有明確的介面,只提供必要的互動,從而允許內部狀態的變更而不破壞依賴於類別結構的程式碼。
  2. 封裝即合約:方法應提供可預測的結果,不應引入意外的副作用。這種合約式的方法增強了類別行為的可預測性,強調了設計的可靠性和強健性。
  3. 可擴充套件性和可維護性:強大的封裝支援更容易的修改和擴充套件,因為其內部運作對外部層是隱藏的。開發人員可以在不影響外部程式碼的情況下引入變更。

透過有效地利用屬性和方法,開發人員可以構建動態且強健的類別,執行複雜的操作,同時保持易用性和模組化。屬性和方法的合理化鼓勵了資料和處理的有組織和邏輯永續性,從而推動了 Python 應用程式的可擴充套件性和可擴充套件性。