使用方法新增行為

已完成

系統的最終目標是產生有用輸出。 為達此目標,您必須處理輸入。 處理時,您可能需要各種方法和資料的協助才能完成作業。 在物件導向程式設計 (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。 此裝飾會指出外觀類似 setterheight() 方法。 新的 height 方法會採用另一個參數 value 作為其第二個參數。

能夠單獨操作高度而不受寬度影響,這仍會產生問題。 您必須先了解類別的功能,再考慮允許 getter 和 setter,因為您會帶來風險。