1. 程式人生 > Python進階應用教學 >04 Python 類的私有屬性和私有方法

04 Python 類的私有屬性和私有方法

在 Python 的面向物件程式設計中,私有屬性是隻能在類的例項方法中訪問的屬性,不允許在外界訪問私有屬性。

1. 私有屬性的定義

1.1 定義

在屬性名稱前加上字首 __,表示該屬性為私有屬性,示例程式碼如下:

class Object:
    def method(self):
        self.__private_attribute = 123

在第 3 行,建立一個私有屬性 __private_attribute

1.2 在類外讀取私有屬性

只能在類的例項方法中訪問私有屬性,不允許在類的外部訪問私有屬性,示例程式碼如下:

class Person:
    def __init__
(self, name): self.__name = name tom = Person('tom') print(tom.__name)
  • 在第 1 行,定義了類 Person
  • 在第 3 行,建立私有屬性 __name
  • 在第 5 行,建立一個例項 tom
  • 在第 6 行,直接訪問例項的屬性 __name

程式執行輸出如下:

Traceback (most recent call last):
  File "attr-get.py", line 6, in <module>
    print(tom.__name)
AttributeError: 'Person'
object has no attribute '__name'

程式執行報錯:‘Person’ object has no attribute ‘__name’,表示無法找到屬性 __name’。因此,在類 Person 的外部無法直接讀取例項的私有屬性

1.3 在類外修改私有屬性

1.2 小節的例子,在類外讀取私有屬性;本節的例子,在類外修改私有屬性。示例程式碼如下:

class Person:
    def __init__(self, name):
        self.__name = name
 
    def get_name(self):
        return
self.__name tom = Person('tom') tom.__name = 'jerry' print(tom.get_name())
  • 在第 1 行,定義了類 Person
    • 在第 3 行,建立私有屬性 __name
    • 在第 6 行,在類的例項方法 get_name 中,訪問私有屬性 __name
  • 在第 8 行,建立一個例項 tom
    • 在第 9 行,將例項的屬性 __name 修改為 ‘jerry’
    • 在第 10 行,通過例項方法 get_name 讀取私有屬性 __name

程式執行輸出如下:

tom

程式在第 9 行,將例項的私有屬性 __name 修改為 ‘jerry’,但是程式輸出表明:在類的內部,私有屬性 __name 沒有發生變化。因此,在類 Person 的外部無法直接修改例項的私有屬性

1.4 通過 set/get 方法訪問私有屬性

本節在類的外部訪問私有屬性的方法,程式碼如下:

class Person:
    def __init__(self, name):
        self.__name = name
 
    def get_name(self):
        return self.__name
 
    def set_name(self, name):
        self.__name = name

tom = Person('tom')
tom.set_name('jerry')
print(tom.get_name())
  • 在第 1 行,定義了類 Person
    • 在第 3 行,建立私有屬性 __name
    • 在第 5 行,建立方法 get_name,它讀取私有屬性 __name
    • 在第 8 行,建立方法 set_name,它修改私有屬性 __name
  • 在第 11 行,建立一個例項 tom
    • 在第 12 行,通過例項方法 get_name 讀取私有屬性 __name
    • 在第 13 行,通過例項方法 set_name 修改私有屬性 __name

程式輸出結果如下:

jerry

程式輸出表明,通過方法 tom.set_name(‘jerry’) 成功的將私有屬性 __name 設定為 jerry。因此,在類的外部通過 get/set 方法訪問私有屬性

2. 私有屬性的應用

2.1 概述

數學中的線段擁有 3 個屬性:

  • start,表示開始位置
  • end,表示結束位置
  • length,表示線段的長度,等於 end - start

當修改屬性 start 時,屬性 length 會發生變化;當修改屬性 end 時,屬性 length 也會發生變化;如果修改屬性 start 或者 end 時,忘記修改屬性 length 的話,則會造成邏輯錯誤,示例程式碼如下:

class Segment:
    def __init__(self, start, end):
        self.start = start
        self.end = end
        self.length = self.end - self.start

    def show(self):
        print('start = %d, end = %d, length = %d' % (self.start, self.end, self.length))

segment = Segment(10, 100)
segment.start = 20
segment.show()
  • 在第 2 行,定義構造方法
    • 在第 5 行,使用屬性 start 和 end 計算屬性 length
  • 在第 7 行,定義方法 show,列印屬性 start、end、length
  • 在第 10 行,建立線段 segment,設定 start = 10,end = 100
  • 在第 11 行,將 start 修改為 20
  • 在第 12 行,呼叫方法 show 列印屬性 start、end、length

程式執行輸出結果如下:

start = 20, end = 100, length = 90

start 修改為 20 後,length 應該等於 80,但是程式輸出表明 length 等於 90。由於 start 修改後,忘記修改 length,造成了這樣的邏輯錯誤

2.2 使用私有屬性解決問題

為了解決上個小節中的問題,將屬性 start、end、length 設定為私有屬性:

  • 禁止在外界直接訪問這 3 個屬性
  • 只能通過對應的 get/set 方法訪問這 3 個屬性
class Segment:
    def __init__(self, start, end):
        self.__start = start
        self.__end = end
        self.__length = self.__end - self.__start

    def get_start(self):
        return self.__start

    def set_start(self, start):
        self.__start = start
        self.__length = self.__end - self.__start

    def get_end(self):
        return self.__end

    def set_end(self, end):
        self.__end = end
        self.__length = self.__end - self.__start

    def get_length(self):
        return self.__start

    def set_length(self, length):
        self.__length = length

    def show(self):
        print('start = %d, end = %d, length = %d' % (self.__start, self.__end, self.__length))

segment = Segment(10, 100)
segment.set_start(20)
segment.show()

類 Segment,包含 3 個私有屬性,讀寫這些屬性的方法如下:

方法 功能
get_start 讀取屬性 start
set_start 設定屬性 start
get_end 讀取屬性 end
set_end 設定屬性 end
get_length 讀取屬性 length
set_length 設定屬性 length
  • 在第 12 行,在 set_start 方法中,修改屬性 start 時,同時重新計算屬性 length
  • 在第 19 行,在 set_end 方法中,修改屬性 end 時,同時重新計算屬性 length
  • 在 set 方法中,修改 start 和 end 屬性時,同時修改 length 屬性,從而保證了一致性,不會出現上個小節中的邏輯錯誤。

程式執行輸出結果如下:

start = 20, end = 100, length = 80

輸出表明,當屬性 start 修改為 20 後,屬性 length 被修改為 80,避免了上個小節中的錯誤。

3. 私有方法的定義

3.1 定義

在Python 的面向物件程式設計中, 私有方法是隻能在類的例項方法中訪問的方法,不允許在外界訪問私有方法。在方法名稱前加上字首 __,表示該方法為私有方法,示例程式碼如下:

class Object:
    def __private_method(self):
        pass

在第 3 行,定義了一個私有方法 __private_method。

3.2 在類外訪問私有方法

私有方法只能在類的內部被呼叫,不允許在類的外部訪問。示例程式碼如下:

class Object:
    def __private_method(self):
        pass

object = Object()        
object.__private_method()
  • 在第 2 行,定義了一個私有方法 __private_method
  • 在第 5 行,建立一個例項 object
  • 在第 6 行,呼叫例項的私有方法 __private_method

程式執行輸出如下:

Traceback (most recent call last):
  File "method-error.py", line 6, in <module>
    object.__private_method()
AttributeError: 'Object' object has no attribute '__private_method'

程式執行報錯:‘Object’ object has no attribute ‘__private_method’,表示無法找到方法 __private_method’。因此,在類 Person 的外部無法呼叫例項的私有方法

4. 私有方法的應用

4.1 概述

本節完成一個分析文字的程式,文字由多個單詞構成,單詞之間使用空格隔開,單詞的型別如下:

  • 數字,例如 123
  • 字母,例如 abc
  • 操作符,例如 =、+、- 等符號

程式對文字分析後,輸出單詞的型別和值,假設輸入文字為 a = 123,則輸出如下:

alpha    a
operator =
digit    123

程式的思路如下:

  1. 定義方法 parse_string,它將文字分割為多個單詞
  2. 定義方法 parse_word,它判斷並列印單詞的型別
  3. 在方法 parse_string 中呼叫 parse_word 處理每個單詞

方法 parse_word 用於輔助實現方法 parse_string,不需要被外界訪問,因此將其設定為私有方法。

4.2 使用私有方法解決問題

class Parser:
    def __parse_word(self, word):
        if word.isdigit():
            print('digit    %s' % word)
        elif word.isalpha():
            print('alpha    %s' % word)
        elif word == '=' or word == '+' or word == '-':
            print('operator %s' % word)

    def parse_string(self, string):
        words = string.split(' ')
        for word in words:
            self.__parse_word(word)

parser = Parser()
parser.parse_string('sum = sum + 100')
  • 在第 2 行,定義私有方法 __parse_word,判斷單詞的型別
    • 在第 3 行,通過方法 isdigit 判斷是否為數字
    • 在第 5 行, 通過方法 isalpha 判斷是否為字母
  • 在第 10 行,定義公開方法 parse_string
    • 在第 11 行,使用 split 將文字分割為多個單詞
    • 在第 13 行,迴圈呼叫私有方法 __parse_word 處理每個單詞
  • 在第 16 行,在類 Parser 的外部,呼叫公開方法 parse_string

實現方法 parse_string 是類 Parser 的介面,外界通過這個方法實現分析文字的功能;而方法 __parse_word 是一個輔助方法,它用於實現方法 parse_string,不需要公開給外界呼叫,因此將它設定為私有方法

程式執行輸出如下:

alpha    sum
operator =
alpha    sum
operator +
digit    100