不間斷 Python 挑戰 Day 22 - 物件導向程式設計:封裝 (Encapsulation)

更新於 發佈於 閱讀時間約 9 分鐘

類別一節中,我們可以用Student類別的實體來存取類別中的name變數、score字典、以及其中的所有方法,這些可以被類別以外的程式碼所直接存取的屬性稱為公有屬性(public attribute)、可以被類別以外的程式碼所直接呼叫的方法稱為公有方法(public method)。事實上,有時我們不希望所有類別內部實作的細節被外部知道,一方面我們希望可以把類別當作一個「黑盒子」,把一個功能單位包裝起來,僅透過統一的介面被外部取用,以方便維護;另一方面我們也不希望外部的程式碼可以隨意修改類別內部的屬性或呼叫方法,導致類別資料或運算邏輯的錯誤。

Python提供物件導向程式設計的另一個特色是封裝,利用封裝的概念,我們可以設計僅供類別內部引用的私有屬性(private attribute)與私有方法(private method),可一定程度防止此類的屬性與方法被外部程式碼輕易地誤用。

私有屬性

將類別中的屬性改為私有屬性的方法是在屬性名稱前面加上雙底線「__」,我們改寫在類別一節中的Student類別,將name與score屬性改為私有屬性。

class Student:

  # 初始化
  def __init__(self, name):
    # 私有屬性
    self.__name = name
    self.__score = {"Math": 0, "Physics": 0, "Chemistry": 0}

# 建立物件
student = Student("Jack")

# 嘗試存取私有屬性
print(student.__name)

此時,若我們想從外部直接存取私有屬性student.__name與student.__score便會發生錯誤。

Traceback (most recent call last):
File "C:\Users\wjweng\PycharmProjects\marathon_python\venv\marathon_python_day22.py", line 32, in <module>
print(student.__name)
AttributeError: 'Student' object has no attribute '__name'

要存取私有屬性有以下幾種方式:

  • 使用公有方法設定或回傳:在類別的內部新增方法設定或回傳私有屬性。
# 回傳名字
def get_name(self):
  return self.__name

# 設定名字
def set_name(self, name):
  self.__name = name

使用方式:

# 設定名字為Rose
student.set_name("Rose")

# 印出名字
print(student.get_name())
  • 使用property()方法設定或回傳:利用上述的公有方法搭配property()方法建立新屬性,可藉由此新屬性來設定或回傳私有屬性。
# 回傳名字
def get_name(self):
  return self.__name

# 設定名字
def set_name(self, name):
  self.__name = name

name = property(get_name, set_name)

使用方式:

# 設定名字為Jack
student.name = "Jack"

# 印出名字
print(student.name)
  • 將get_name()與set_name()方法改為相同的名稱name(),使用裝飾器@property將name()轉換為可讀取的屬性、使用裝飾器@name.setter來設定名字。
@property
def name(self):
  return self.__name
@name.setter
def name(self, name):
  self.__name = name

使用方式:

# 設定名字為Jack
student.name = "Jack"

# 印出名字
print(student.name)

然而,這個__name屬性到哪裡去了呢?我們可以用物件的__dict__屬性來看一下,__dict__是用來儲存物件屬性的一個字典,其鍵為屬性名、值為屬性的值。

print(student.__dict__)

可以看到這個私有屬性還在,只是被改名為_Student__name。

{'_Student__name': 'Jack', '_Student__score': {'Math': 0, 'Physics': 0, 'Chemistry': 0}}

因此,我們還是可以透過此名稱來存取私有屬性,但至少可以防止私有屬性被外部程式碼意外地存取或修改。

print(student._Student__name)

執行結果:

Jack

私有方法

將類別中的方法改為私有方法的方式和私有屬性相同,是在方法的名稱前面加上雙底線「__」,延續上方的範例,我們將原有的add_subject方法改為私有方法__add_subject,並在set_score方法中呼叫它。

class Student:

  # 初始化
  def __init__(self, name):
    # 私有屬性
    self.__name = name
    self.__score = {"Math": 0, "Physics": 0, "Chemistry": 0}

  # 私有方法
  def __add_subject(self, subject):
    self.__score[subject] = 0

  # 公有方法
  def set_score(self, subject, score):
    if subject not in self.__score:
      self.__add_subject(subject)
    self.__score[subject] = score

  def get_subject(self):
    for key in self.__score:
      print(key)

# 建立物件
student = Student("Jack")

# 新增科目
student.__add_subject("Art")

此時,若我們想從外部直接呼叫__add_subject方法便會發生錯誤。

Traceback (most recent call last):
File "C:\Users\wjweng\PycharmProjects\marathon_python\venv\marathon_python_day22.py", line 40, in <module>
student.__add_subject("Art")
AttributeError: 'Student' object has no attribute '__add_subject'

透過set_score方法,我們可以在類別內部呼叫__add_subject方法。

# 登錄分數
student.set_score("Art", 80)

# 取得分數
student.get_score("Art")

執行結果:

80

然而,和私有屬性同樣的方式,我們依然可以透過被改名後的方法名稱,從外部直接呼叫私有方法。

# 呼叫私有方法
student._Student__add_subject("Music")

# 取得現有科目
student.get_subject()

執行結果:

Math
Physics
Chemistry
Art
Music

程式範例

本文程式範例收錄於:
https://github.com/wjweng/marathon_python/blob/master/Day1_to_25/marathon_python_day22.py

留言
avatar-img
留言分享你的想法!
avatar-img
Wei-Jie Weng的沙龍
48會員
36內容數
Wei-Jie Weng的沙龍的其他內容
2022/07/13
對於程式的初學者而言,理解程式的流程、迴圈的進行、或是變數的變化會需要一定程度將程式在腦中進行運算的能力,要一段時間熟悉與適應,尤其是當程式執行的結果不如預期時,往往是計算的過程和自己所想像的不同,這時又更難靠自己的能力找出錯誤。因此,這邊要介紹的這個工具可以將程式執行的過程逐行將變數的變化視覺化地
Thumbnail
2022/07/13
對於程式的初學者而言,理解程式的流程、迴圈的進行、或是變數的變化會需要一定程度將程式在腦中進行運算的能力,要一段時間熟悉與適應,尤其是當程式執行的結果不如預期時,往往是計算的過程和自己所想像的不同,這時又更難靠自己的能力找出錯誤。因此,這邊要介紹的這個工具可以將程式執行的過程逐行將變數的變化視覺化地
Thumbnail
2022/07/13
在上一節介紹了 JSON 資料的基本架構後,我們將改寫並擴充密碼產生器程式,讓它能夠藉由 JSON 的資料結構完成帳密搜尋的功能。
Thumbnail
2022/07/13
在上一節介紹了 JSON 資料的基本架構後,我們將改寫並擴充密碼產生器程式,讓它能夠藉由 JSON 的資料結構完成帳密搜尋的功能。
Thumbnail
2022/06/23
JSON的全名叫JavaScript Object Notation,是由Douglas Crockford所設計的一種資料格式,最初應用在JavaScript程式語言中,做為一種資料交換的格式,而後被廣泛運用在Web開發與NoSQL資料庫,現今已成為一種重要的資料格式。
Thumbnail
2022/06/23
JSON的全名叫JavaScript Object Notation,是由Douglas Crockford所設計的一種資料格式,最初應用在JavaScript程式語言中,做為一種資料交換的格式,而後被廣泛運用在Web開發與NoSQL資料庫,現今已成為一種重要的資料格式。
Thumbnail
看更多
你可能也想看
Thumbnail
Kotlin 中存取類別的屬性是一個重要的操作,透過 getter 與 setter 宣告方式讓我們可以使用 var 或 val 來宣告屬性。本篇文章介紹了在類別中宣告屬性的範例以及存取與修改屬性的操作。
Thumbnail
Kotlin 中存取類別的屬性是一個重要的操作,透過 getter 與 setter 宣告方式讓我們可以使用 var 或 val 來宣告屬性。本篇文章介紹了在類別中宣告屬性的範例以及存取與修改屬性的操作。
Thumbnail
Immutable interface 讓封裝更有彈性,不用擔心 setter 的過度開放。當不希望物件被不允許的對象修改時,只需讓對方取得 getter 的介面即可,反之,讓能夠允許修改的對象取得有 setter 的物件即可。
Thumbnail
Immutable interface 讓封裝更有彈性,不用擔心 setter 的過度開放。當不希望物件被不允許的對象修改時,只需讓對方取得 getter 的介面即可,反之,讓能夠允許修改的對象取得有 setter 的物件即可。
Thumbnail
Python是一種物件導向的程式語言,它讓我們可以使用類別(Class)來定義和創建物件,想像一個物件就像是一個實體,可以包含資料和功能。 舉例來說,一個人可以被視為一個物件,它有姓名、年齡等資料,還可以說話、走路等功能。類別就像是一個模板,它描述了物件的特徵和行為。 Python中,我們使用
Thumbnail
Python是一種物件導向的程式語言,它讓我們可以使用類別(Class)來定義和創建物件,想像一個物件就像是一個實體,可以包含資料和功能。 舉例來說,一個人可以被視為一個物件,它有姓名、年齡等資料,還可以說話、走路等功能。類別就像是一個模板,它描述了物件的特徵和行為。 Python中,我們使用
Thumbnail
二、繼承(inheritance) 繼承就是假如A(子)類別去繼承B(父)類別,那麼A(子)類別可以直接去使用B(父)類別非私有的屬性和方法,但是A(子)只能繼承一個B(父)類別ㄛ! 一樣的道理可以比喻為:爸爸跟小孩之間的關係。小孩可以去運用爸爸的資源,但是爸爸的工作屬於他自己的不能跟小孩一起分享,
Thumbnail
二、繼承(inheritance) 繼承就是假如A(子)類別去繼承B(父)類別,那麼A(子)類別可以直接去使用B(父)類別非私有的屬性和方法,但是A(子)只能繼承一個B(父)類別ㄛ! 一樣的道理可以比喻為:爸爸跟小孩之間的關係。小孩可以去運用爸爸的資源,但是爸爸的工作屬於他自己的不能跟小孩一起分享,
Thumbnail
每當聽到學生說自己英文很差,或是不會畫圖時,我心裡都會百感交集。 聽在耳裡,似曾相似。 我曾經也很喜歡先定義自己。 明明心裡想做一件事,下一秒就像搭上時光機一樣,自動擷取某段經驗,接著就是打著退堂鼓,看著心中的火苗漸漸熄滅。 近幾年才開始認識真正的自己,透過教學和創作。 大學時期的自己,上台報告和試
Thumbnail
每當聽到學生說自己英文很差,或是不會畫圖時,我心裡都會百感交集。 聽在耳裡,似曾相似。 我曾經也很喜歡先定義自己。 明明心裡想做一件事,下一秒就像搭上時光機一樣,自動擷取某段經驗,接著就是打著退堂鼓,看著心中的火苗漸漸熄滅。 近幾年才開始認識真正的自己,透過教學和創作。 大學時期的自己,上台報告和試
Thumbnail
單一職責原則(Single Responsibility Principle) 里氏替換原則(Liskov Substitution Principle) 依賴反轉原則(Dependence Inversion Principle) 最少知識原則(得墨忒耳定律)(Law Of Demeter)
Thumbnail
單一職責原則(Single Responsibility Principle) 里氏替換原則(Liskov Substitution Principle) 依賴反轉原則(Dependence Inversion Principle) 最少知識原則(得墨忒耳定律)(Law Of Demeter)
Thumbnail
在類別一節中,我們可以用Student類別的實體來存取類別中的name變數、score字典、以及其中的所有方法,這些可以被類別以外的程式碼所直接存取的屬性稱為公有屬性(public attribute)、可以被類別以外的程式碼所直接呼叫的方法稱為公有方法(public method)。
Thumbnail
在類別一節中,我們可以用Student類別的實體來存取類別中的name變數、score字典、以及其中的所有方法,這些可以被類別以外的程式碼所直接存取的屬性稱為公有屬性(public attribute)、可以被類別以外的程式碼所直接呼叫的方法稱為公有方法(public method)。
Thumbnail
在Python中,所有東西都是物件。執行程式碼a = 5,會建立5這個物件,然後給a一個reference,這個reference就是告訴a,它的值放在記憶體的哪個位置,要用時,就到那裡去拿。 看到這裡,很直覺的反應是:這不就是C裡頭的pointer嗎?!然後就沒再多想了。
Thumbnail
在Python中,所有東西都是物件。執行程式碼a = 5,會建立5這個物件,然後給a一個reference,這個reference就是告訴a,它的值放在記憶體的哪個位置,要用時,就到那裡去拿。 看到這裡,很直覺的反應是:這不就是C裡頭的pointer嗎?!然後就沒再多想了。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News