使用方法新增行為
系統的最終目標是產生有用輸出。 為達此目標,您必須處理輸入。 處理時,您可能需要各種方法和資料的協助才能完成作業。 在物件導向程式設計 (OOP) 中,您的方法和資料會放在物件上。 在 OOP 中,您需要方法才能處理輸入並產生結果。
OOP 中的方法
無論使用何種範例,方法都可以執行動作。 該動作可以是只依賴輸入的計算,或可以變更變數值。
在 OOP 中,物件上的方法有兩種:
- 外部方法,其他物件可以叫用。
- 內部方法,其他物件無法觸及。 而且,內部方法有利於執行由叫用外部方法所啟動的工作。
不論方法的類型為何,都可以變更物件屬性的值,亦即其狀態。
狀態的概念,以及「誰」和「什麼」可以變更狀態,都是重要的主題。 這是設計類別和物件的重要部分。 這些問題將帶領我們進入下一節:<封裝>。
封裝: 保護您的資料
對封裝的一般概念是,物件上的資料為「內部」,其只與物件相關。 物件和方法需要有資料才能執行其工作,也就是執行工作。 當表示資料為內部時,您的意思其實是應該保護資料免受其他外部人士的操作,而不是不受控制的外部操作。 問題是其原因為何?
為何需要它
讓我們說明一下為何不能讓其他物件直接觸及資料的原因。 以下列出一些範例:
您不需要知道內部情況。 當駕駛汽車時,您會踩下踏板以控制離合器或加速或剎車。 因為您是以較高的視野操作車輛,所以您不在意檯面下發生了什麼,汽車如何執行動作。 程式碼也一樣。 大多數時候,您不需要知道物件如何執行某項作業,只要有可以叫用的方法執行所想要的工作即可。
您不應該知道內部情況。 想像用螺絲起子或焊接工具嘗試加速,而不是用腳踏板與汽車互動。 聽起來很可怕,對吧? 因為真的很可怕啊。 或者,假設一個更具體的範例,一個包含下列程式碼的正方形類別:
class Square: def __init__(self): self.height = 2 self.width = 2 def set_side(self, new_side): self.height = new_side self.width = new_side square = Square() square.height = 3 # not a square anymore
在此正方形範例中,您透過設定
height
變數破壞了正方形的概念。 此正方形的編碼方式,其需要您叫用set_side()
方法,讓正方形正常顯現。 讓物件自己處理自己的資料較為安全。 幾乎在每個執行個體中,您都要在透過方法進行互動還是明確設定資料中二選一。
存取層級
如何保護類別和物件免於不想要的資料操作? 答案是使用「存取層級」。 您可使用特定關鍵字標記資料和函式,並向外部世界和其他物件隱藏資料。 這些關鍵字稱為存取修飾詞。
Python 隱藏資料的方式是在屬性名稱中新增前置詞。 一個前置底線 (_
) 是告訴外部世界可能不該觸及這項資料。 當修改正方形類別時,最後會使用下列程式碼:
class Square:
def __init__(self):
self._height = 2
self._width = 2
def set_side(self, new_side):
self._height = new_side
self._width = new_side
square = Square()
square._height = 3 # not a square anymore
一個前置底線仍然可修改資料,這在 Python 表示為「受保護」。 還可以做得更好嗎? 當然可以,我們可以使用兩個前置底線 (__
) 來表示為「私用」。 您的正方形類別現在應該看起來類似以下程式碼:
class Square:
def __init__(self):
self.__height = 2
self.__width = 2
def set_side(self, new_side):
self.__height = new_side
self.__width = new_side
square = Square()
square.__height = 3 # raises AttributeError
很好,這樣就安全了。 這樣資料就受到保護了嗎? 嗯,不完全。 Python 只是變更基礎資料的名稱。 只要輸入此程式碼,您仍然可變更其值:
square = Square()
square._Square__height = 3 # is allowed
許多其他實作資料保護的語言都會以不同方式解決此問題。 Python 在資料保護方面的獨特之處更像是建議層級,而不是嚴格實行。
getter 和 setter 是什麼?
一直以來,我們都堅持不該讓外部觸及資料。 資料是物件的考量。 但如同所有規則和強烈建議一樣,總有例外狀況。 有時候您需要變更資料,或變更資料比必須新增大量程式碼更簡單。
getter 和 setter (也稱為「存取子」和「更動子」) 是專門用來讀取或變更資料的方法。 getter 的職責是讓外部可讀取內部資料,這聽起來滿不錯的,不是嗎? setter 則是可直接變更資料的方法。 此概念是讓 setter 擔任防護,使無法設定「不正確的」值。 讓我們再次叫出正方形類別,看看 getter 和 setter 如何作用:
class Square:
def __init__(self):
self.__height = 2
self.__width = 2
def set_side(self, new_side):
self.__height = new_side
self.__width = new_side
def get_height(self):
return self.__height
def set_height(self, h):
if h >= 0:
self.__height = h
else:
raise Exception("value needs to be 0 or larger")
square = Square()
square.__height = 3 # raises AttributeError
set_height()
方法會阻止您將值設定為負值。 如果設定負值,就會引發例外狀況。
對 getter 和 setter 使用裝飾項目
裝飾項目是 Python 的重要主體。 其屬於較大的主體,稱為「中繼程式設計」。 裝飾項目是將函式作為輸入的函式。 其概念是將可重複使用的功能編碼為「裝飾項目函式」,然後用以「裝飾」其他函式。 旨在為函式提供過去沒有的功能。 例如,裝飾項目可將欄位新增至物件、測量叫用函式所需的時間,以及執行更多功能。
在 OOP 以及 getter 和 setter 的內容中,有個特定的裝飾項目 @property
可有助在新增 getter 和 setter 時移除一些未定案程式碼。 @property
裝飾項目會執行下列動作:
- 建立支援欄位: 當使用
@property
裝飾項目裝飾函式時,其會建立私用的支援欄位。 您可隨時覆寫此行為,但有預設行為其實還不錯。 - 識別 setter: setter 方法可變更支援欄位。
- 識別 getter: 此函式應該會傳回支援欄位。
- 識別 delete 函式: 此函式可刪除欄位。
讓我們看看此裝飾項目的效果:
class Square:
def __init__(self, w, h):
self.__height = h
self.__width = w
def set_side(self, new_side):
self.__height = new_side
self.__width = new_side
@property
def height(self):
return self.__height
@height.setter
def height(self, new_value):
if new_value >= 0:
self.__height = new_value
else:
raise Exception("Value must be larger than 0")
在上述程式碼中,函式 height()
裝飾了裝飾項目 @property
。 這個「裝飾」動作會建立私用欄位 __height
。 因為裝飾項目已定義 __height
欄位,所以 __init__()
中未予定義。 還出現一個裝飾,即 @height.setter
。 此裝飾會指出外觀類似 setter 的 height()
方法。 新的 height 方法會採用另一個參數 value
作為其第二個參數。
能夠單獨操作高度而不受寬度影響,這仍會產生問題。 您必須先了解類別的功能,再考慮允許 getter 和 setter,因為您會帶來風險。