iT邦幫忙

2024 iThome 鐵人賽

DAY 14
1
Python

從概念到應用:Python實戰開發學習之旅系列 第 14

[Day13] 第二週 - 進階工程師的開端 - 物件導向之三大特性

  • 分享至 

  • xImage
  •  

目標

昨天
我們介紹介紹了python的Class的建立方法
今天我們會詳細介紹,物件導向的特性(用python實作)

1.封裝Encapsulation.
2.繼承Inheritance.
3.多型Polymorphism.

特性 優點 缺點 使用場景
封裝 提高安全性、可維護性和模組化 封裝過多會增加程式的複雜性 資料隱藏、保護敏感資訊
繼承 代碼重用、高效維護 繼承結構複雜時難以理解,子類與父類耦合緊密 建立層次化的物件模型
多型 增強靈活性和可擴展性,提供抽象層次 使用過多會增加程式的調試難度和執行時間 需要不同類別有相同介面但不同行為時

介紹完特性之後會帶範例讓大家更理解物件導向的使用場景。
畢竟我是圖片控,我會盡量用流程或架構的圖讓大家理解Class
/images/emoticon/emoticon39.gif

Parts1 物件導向三大特性 - 封裝

定義

封裝(Encapsulation)是物件導向程式設計中的一個核心概念。它的主要目的是將資料(屬性)和行為(方法)封裝在一起,並且對外部程式隱藏物件的內部實現細節,只提供外部所需的介面。這樣可以增強程式的安全性和可維護性。

封裝的另一個好處是可以限制對物件內部屬性的直接訪問,並透過設置特定的 getter 和 setter 方法來控制屬性的讀取和修改,這樣可以確保物件內部狀態的正確性

需求分析

1.今天我是一間公司老闆,我需要定義employee class
2.那們員工就會有salary屬性
3.薪水在公司裏面是機密資料所以需要定義私有屬性跟方法
4.透過前面的特性來實作getter(取得薪水) setter(設定薪水) 來操作

https://ithelp.ithome.com.tw/upload/images/20240928/20121052GxDAlXQNu1.png

實作員工薪水讀取class

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self._salary = salary  # 使用私有屬性保存薪水數值

    # 使用 property 修飾詞來定義 getter 方法
    @property
    def salary(self):
        return self._salary

    # 使用 setter 修飾詞來定義 setter 方法
    @salary.setter
    def salary(self, value):
        if value < 0:
            raise ValueError("薪水不能為負數")  # 設定薪水時檢查合理性
        self._salary = value  # 如果合理,則更新薪水

    def display_info(self):
        print(f"員工: {self.name}, 薪水: {self.salary}")

# 使用範例
emp = Employee("Alice", 5000)  # 初始化一位員工 Alice,薪水為 5000
emp.display_info()              # 輸出: 員工: Alice, 薪水: 5000
emp.salary = 6000               # 使用 setter 更新薪水
emp.display_info()              # 輸出: 員工: Alice, 薪水: 6000
# emp.salary = -1000            # 如果嘗試設定負值,會引發 ValueError

修改點解釋:
@property:
把 salary 方法變成屬性,讓它看起來像是在直接訪問 salary,但實際上會調用 getter 方法。
@salary.setter:
當您嘗試修改 salary 時,會自動調用這個 setter 方法。在這裡,我們可以做一些邏輯檢查,例如防止薪水設定為負數。

self._salary:
使用了以 _ 開頭的變數名,代表這是一個「私有屬性」。通常這只是約定俗成的規範,用來表示該變數不應該在類別外部直接訪問。

通過封裝的優點:

  • 防止不當修改:將屬性設為私有,防止外部隨意更改數據。
  • 驗證輸入:通過 setter 方法,可以在修改屬性時添加檢查邏輯,確保輸入合法。
  • 提升代碼維護性:減少錯誤輸入導致的程式崩潰或邏輯錯誤。

封裝是保證類的內部狀態不被外部代碼隨意破壞的關鍵手段。

tips - 修飾詞的補充(decorators)

  1. 意義
    Python 中的修飾詞(decorators)通常是加在函數或方法前面的。這種語法使用 @ 符號,後面跟隨修飾詞的名稱,這樣可以在不改變原始函數代碼的情況下為其添加額外的功能。

2.範例

原本函數加上修飾詞

@decorator_name
def function_to_decorate():
    # 函數內容

同等於

def function_to_decorate():
    # 函數內容

function_to_decorate = decorator_name(function_to_decorate)

總結
1.語法位置:修飾詞通常加在函數或方法的前面。
2.功能:修飾詞可以用來添加額外功能,如日誌記錄、性能測試、授權檢查等。
3.簡化代碼:使用修飾詞可以使代碼更簡潔,並提高可讀性和可維護性。
這使得 Python 的修飾詞成為一個非常強大且靈活的編程工具。 => 也有類似Java annotations的概念

Parts2 物件導向三大特性 - 繼承

定義: 簡單來說就是把父類別的屬性跟方法繼承下來使用

實作-貓咪跟狗狗繼承動物的class

這邊會有幾個需求點

  1. 先定義animal動物的class以及speak方法還有name屬性
  2. 定義貓咪跟狗狗兩種不同的class
  3. 狗狗跟貓咪都要可以使用animal的屬性及方法
  4. 狗狗要breed屬性使用

https://ithelp.ithome.com.tw/upload/images/20240928/20121052sUYzn3Iu6S.png

# 定義父類別 Animal
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return "Some sound"

# 定義子類別 Dog 繼承自 Animal
class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)  # 使用 super() 調用父類別的 __init__ 方法
        self.breed = breed

    def speak(self):
        return "Woof!"

# 定義子類別 Cat 繼承自 Animal
class Cat(Animal):
    def speak(self):
        return "Meow!"


# 使用示例
dog = Dog("Rex", "Labrador")
cat = Cat("Whiskers")

print(f"{dog.name} is a {dog.breed} and says: {dog.speak()}")  # 輸出: Rex is a Labrador and says: Woof!
print(f"{cat.name} says: {cat.speak()}")  # 輸出: Whiskers says: Meow!

tips 拆解繼承語法

  1. Python 繼承方法的重點
    繼承的語法:在定義子類別時,父類別寫在小括號中。
class Child(Parent):
    pass
  1. python新增屬性的方法:

在子類別的 __init__ 方法中,可以直接使用 self 關鍵字來新增屬性。
通常使用 super() 來初始化父類別的屬性,以確保父類別的初始化邏輯被執行。

class Animal:
    def __init__(self, species):
        self.species = species

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__('Dog')  # 初始化父類別屬性
        self.name = name
        self.breed = breed

3.python繼承父屬性的方法super()

基本概念:super() 是一個內建函數,用於呼叫父類別的方法或屬性。
使用情境:當你想要在子類別中重用父類別的方法時,可以使用 super(),這樣可以避免直接使用父類別名稱,讓程式碼更靈活且易於維護。

class Parent:
    def __init__(self, name):
        self.name = name

class Child(Parent):
    def __init__(self, name, age):
        super().__init__(name)  # 呼叫父類別的 __init__ 方法
        self.age = age

Parts3 物件導向三大特性 - 多型

定義: 允許不同類別的物件以相同的介面進行操作。這意味著不同類別可以定義相同的方法名稱,但具體的行為會依據物件的類別而有所不同。

白話文就是: 雖然貓咪跟狗狗都是動物,但是叫聲卻不同/images/emoticon/emoticon01.gif

直接以上面貓咪跟狗狗的舉例就是

class Animal:
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        return "Woof!"

class Cat(Animal):
    def speak(self):
        return "Meow!"

# 使用多型
def animal_sound(animal):
    print(animal.speak())

# 創建物件
dog = Dog()
cat = Cat()

# 呼叫 animal_sound 函數,根據物件類別呼叫對應的 speak 方法
animal_sound(dog)  # 輸出: Woof!
animal_sound(cat)   # 輸出: Meow!

這邊就是實作了物件導向多型的概念
貓咪跟狗狗的叫聲都不同~

parts4思考使用Class的時機!?

總結:
1.小專案:不一定需要使用類別,可以用簡單的函數解決問題。
2.大專案:類別設計是很常見且必要的,幫助你更好地組織和管理代碼。
3.OOP 的設計原則在實際應用中能讓程式碼更具可讀性、可維護性、以及擴展性。

範例1 接受簡單的前端CRUD進SQL-不需要使用class

在網頁程式開發中,如果你只需要從前端接收數據並插入到資料庫,理論上你可以不使用類別(class)來處理。你可以直接通過 SQL 語法或資料庫操作來插入資料。

比如說我只操作CRUD塞資料進資料庫

step1接收來自前端的數據
product_name = request.form['name']
product_price = request.form['price']

step2直接執行資料庫插入操作

cursor.execute("INSERT INTO products (name, price) VALUES (%s, %s)", (product_name, product_price))
conn.commit()

範例2 使用ORM套建更靈活的操作資料進資料庫-使用class

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Product(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), nullable=False)
    price = db.Column(db.Float, nullable=False)

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

# 插入數據
new_product = Product(name="Laptop", price=999.99)
db.session.add(new_product)
db.session.commit()

優點:
1.可維護性:ORM 自動處理資料庫操作,省去寫大量 SQL 語句的麻煩。當資料庫結構變化時(如修改表結構),你只需修改類別定義,ORM 會自動適應變更。
2.安全性:ORM 能自動處理 SQL 注入問題,減少手動處理 SQL 的風險。
3.跨資料庫兼容:不同資料庫系統有不同的 SQL 語法,但使用 ORM 可以讓程式碼在不同的資料庫間移植時不需要改變 SQL 語句。

今日總結

以今天要新增一個product範例的話:

何時應該使用 Class? 解釋
業務邏輯複雜 商品除了 nameprice 外,還有更多屬性(如分類、庫存、促銷等),這些屬性之間有邏輯關聯。類別幫助封裝這些屬性和行為。
數據操作頻繁 當需要頻繁進行數據操作(查詢、更新、刪除等),ORM 工具能簡化這些操作,不必每次重寫 SQL 語句。
擴展性 在未來項目擴展時,類別更容易支持新增功能、增加屬性,維持程式碼結構清晰性。
模組化設計與重用 如果系統需要模組化設計(如電商的「訂單」、「用戶」、「商品」等模組),類別能更好地進行封裝和重用,降低耦合度。
減少重複代碼 封裝屬性和行為可以減少重複的業務邏輯處理,提高程式碼可維護性。例如:產品折扣和庫存更新等功能可通過類別方法多次使用。
對象與實體關聯處理 例如商品、用戶、訂單等都可以看作實體(Entity),通過類別來處理實體間的關係和互動,類別封裝能讓這些處理更直觀。
減少錯誤與邏輯清晰 將邏輯封裝進類別後,其他開發者可以通過呼叫類別方法來操作數據,減少直接修改數據庫的錯誤機會,也讓邏輯更集中在一個地方。
測試與持續集成方便 類別化的設計更易於編寫單元測試,確保每個方法運行正常,對於需要自動化測試和持續集成的專案來說,是個優勢。
何時可以不使用 Class? 解釋
操作單一且簡單 如果只是從前端接收數據並插入資料庫,且沒有複雜邏輯,直接使用 SQL 更為直接和高效。
小型專案或輕量級應用 對於小型或簡單的應用,類別和 ORM 會增加額外的複雜性,直接使用 SQL 反而更簡單合適。
臨時性資料處理 如果某些資料處理邏輯只會執行一次或很少的次數,使用函數式編程可能會比封裝成類別更簡潔,避免過度設計。
無複雜邏輯或重複操作 當業務邏輯非常簡單,不需要封裝邏輯,也不會經常操作,直接使用 SQL 或函數可以快速完成需求,無需引入類別。
快速開發的原型 當在進行快速開發原型或小型專案時,類別的設計與結構可能會延緩開發進度,直接操作資料庫與函數處理會更快。
結論 解釋
不使用 class 適用於簡單的網頁應用,直接接收數據並插入資料庫,不需要複雜邏輯操作時,直接使用 SQL。
使用 class 和 ORM 適用於逐漸變複雜的系統,使用類別來定義數據結構,配合 ORM 操作資料庫,讓程式更具可維護性與擴展性。
使用 class 和模組化設計 當系統逐漸擴展,需求越來越多時,模組化的類別設計可更好地組織和封裝邏輯,為未來維護與擴展提供更好的基礎。

大家可以思考來設計屬於自己的最佳程式喔!!
雖然平常都提倡化繁為簡
但有時候開發團隊或是專案越來越大時
還是會以許多封裝或是拆分功能細節控制這樣的觀念去思考~
是不是覺得工程師很搞剛呢XDD/images/emoticon/emoticon01.gif


上一篇
[Day12] 第二週 - 進階工程師的開端 - Class 與物件導向
下一篇
[Day14] 第二週 - 進階工程師的開端 - 提升python效能、品質、維護的小技巧
系列文
從概念到應用:Python實戰開發學習之旅31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言