Python 類(lèi)的私有屬性和私有方法
在 Python 的面向?qū)ο缶幊讨?,私有屬性是只能在?lèi)的實(shí)例方法中訪問(wèn)的屬性,不允許在外界訪問(wèn)私有屬性。
1. 私有屬性的定義
1.1 定義
在屬性名稱前加上前綴 __,表示該屬性為私有屬性,示例代碼如下:
class Object:
def method(self):
self.__private_attribute = 123
在第 3 行,創(chuàng)建一個(gè)私有屬性 __private_attribute
。
1.2 在類(lèi)外讀取私有屬性
只能在類(lèi)的實(shí)例方法中訪問(wèn)私有屬性,不允許在類(lèi)的外部訪問(wèn)私有屬性,示例代碼如下:
class Person:
def __init__(self, name):
self.__name = name
tom = Person('tom')
print(tom.__name)
- 在第 1 行,定義了類(lèi) Person
- 在第 3 行,創(chuàng)建私有屬性 __name
- 在第 5 行,創(chuàng)建一個(gè)實(shí)例 tom
- 在第 6 行,直接訪問(wèn)實(shí)例的屬性 __name
程序運(yùn)行輸出如下:
Traceback (most recent call last):
File "attr-get.py", line 6, in <module>
print(tom.__name)
AttributeError: 'Person' object has no attribute '__name'
程序運(yùn)行報(bào)錯(cuò):‘Person’ object has no attribute ‘__name’,表示無(wú)法找到屬性 __name’。因此,在類(lèi) Person 的外部無(wú)法直接讀取實(shí)例的私有屬性。
1.3 在類(lèi)外修改私有屬性
1.2 小節(jié)的例子,在類(lèi)外讀取私有屬性;本節(jié)的例子,在類(lèi)外修改私有屬性。示例代碼如下:
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 行,定義了類(lèi) Person
- 在第 3 行,創(chuàng)建私有屬性 __name
- 在第 6 行,在類(lèi)的實(shí)例方法 get_name 中,訪問(wèn)私有屬性 __name
- 在第 8 行,創(chuàng)建一個(gè)實(shí)例 tom
- 在第 9 行,將實(shí)例的屬性 __name 修改為 ‘jerry’
- 在第 10 行,通過(guò)實(shí)例方法 get_name 讀取私有屬性 __name
程序運(yùn)行輸出如下:
tom
程序在第 9 行,將實(shí)例的私有屬性 __name 修改為 ‘jerry’,但是程序輸出表明:在類(lèi)的內(nèi)部,私有屬性 __name 沒(méi)有發(fā)生變化。因此,在類(lèi) Person 的外部無(wú)法直接修改實(shí)例的私有屬性。
1.4 通過(guò) set/get 方法訪問(wèn)私有屬性
本節(jié)在類(lèi)的外部訪問(wèn)私有屬性的方法,代碼如下:
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 行,定義了類(lèi) Person
- 在第 3 行,創(chuàng)建私有屬性 __name
- 在第 5 行,創(chuàng)建方法 get_name,它讀取私有屬性 __name
- 在第 8 行,創(chuàng)建方法 set_name,它修改私有屬性 __name
- 在第 11 行,創(chuàng)建一個(gè)實(shí)例 tom
- 在第 12 行,通過(guò)實(shí)例方法 get_name 讀取私有屬性 __name
- 在第 13 行,通過(guò)實(shí)例方法 set_name 修改私有屬性 __name
程序輸出結(jié)果如下:
jerry
程序輸出表明,通過(guò)方法 tom.set_name(‘jerry’) 成功的將私有屬性 __name 設(shè)置為 jerry。因此,在類(lèi)的外部通過(guò) get/set 方法訪問(wèn)私有屬性。
2. 私有屬性的應(yīng)用
2.1 概述
數(shù)學(xué)中的線段擁有 3 個(gè)屬性:
- start,表示開(kāi)始位置
- end,表示結(jié)束位置
- length,表示線段的長(zhǎng)度,等于 end - start
當(dāng)修改屬性 start 時(shí),屬性 length 會(huì)發(fā)生變化;當(dāng)修改屬性 end 時(shí),屬性 length 也會(huì)發(fā)生變化;如果修改屬性 start 或者 end 時(shí),忘記修改屬性 length 的話,則會(huì)造成邏輯錯(cuò)誤,示例代碼如下:
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 行,定義構(gòu)造方法
- 在第 5 行,使用屬性 start 和 end 計(jì)算屬性 length
- 在第 7 行,定義方法 show,打印屬性 start、end、length
- 在第 10 行,創(chuàng)建線段 segment,設(shè)置 start = 10,end = 100
- 在第 11 行,將 start 修改為 20
- 在第 12 行,調(diào)用方法 show 打印屬性 start、end、length
程序運(yùn)行輸出結(jié)果如下:
start = 20, end = 100, length = 90
start 修改為 20 后,length 應(yīng)該等于 80,但是程序輸出表明 length 等于 90。由于 start 修改后,忘記修改 length,造成了這樣的邏輯錯(cuò)誤。
2.2 使用私有屬性解決問(wèn)題
為了解決上個(gè)小節(jié)中的問(wèn)題,將屬性 start、end、length 設(shè)置為私有屬性:
- 禁止在外界直接訪問(wèn)這 3 個(gè)屬性
- 只能通過(guò)對(duì)應(yīng)的 get/set 方法訪問(wèn)這 3 個(gè)屬性
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()
類(lèi) Segment,包含 3 個(gè)私有屬性,讀寫(xiě)這些屬性的方法如下:
方法 | 功能 |
---|---|
get_start | 讀取屬性 start |
set_start | 設(shè)置屬性 start |
get_end | 讀取屬性 end |
set_end | 設(shè)置屬性 end |
get_length | 讀取屬性 length |
set_length | 設(shè)置屬性 length |
- 在第 12 行,在 set_start 方法中,修改屬性 start 時(shí),同時(shí)重新計(jì)算屬性 length
- 在第 19 行,在 set_end 方法中,修改屬性 end 時(shí),同時(shí)重新計(jì)算屬性 length
- 在 set 方法中,修改 start 和 end 屬性時(shí),同時(shí)修改 length 屬性,從而保證了一致性,不會(huì)出現(xiàn)上個(gè)小節(jié)中的邏輯錯(cuò)誤。
程序運(yùn)行輸出結(jié)果如下:
start = 20, end = 100, length = 80
輸出表明,當(dāng)屬性 start 修改為 20 后,屬性 length 被修改為 80,避免了上個(gè)小節(jié)中的錯(cuò)誤。
3. 私有方法的定義
3.1 定義
在Python 的面向?qū)ο缶幊讨? 私有方法是只能在類(lèi)的實(shí)例方法中訪問(wèn)的方法,不允許在外界訪問(wèn)私有方法。在方法名稱前加上前綴 __,表示該方法為私有方法,示例代碼如下:
class Object:
def __private_method(self):
pass
在第 3 行,定義了一個(gè)私有方法 __private_method。
3.2 在類(lèi)外訪問(wèn)私有方法
私有方法只能在類(lèi)的內(nèi)部被調(diào)用,不允許在類(lèi)的外部訪問(wèn)。示例代碼如下:
class Object:
def __private_method(self):
pass
object = Object()
object.__private_method()
- 在第 2 行,定義了一個(gè)私有方法 __private_method
- 在第 5 行,創(chuàng)建一個(gè)實(shí)例 object
- 在第 6 行,調(diào)用實(shí)例的私有方法 __private_method
程序運(yùn)行輸出如下:
Traceback (most recent call last):
File "method-error.py", line 6, in <module>
object.__private_method()
AttributeError: 'Object' object has no attribute '__private_method'
程序運(yùn)行報(bào)錯(cuò):‘Object’ object has no attribute ‘__private_method’,表示無(wú)法找到方法 __private_method’。因此,在類(lèi) Person 的外部無(wú)法調(diào)用實(shí)例的私有方法。
4. 私有方法的應(yīng)用
4.1 概述
本節(jié)完成一個(gè)分析文本的程序,文本由多個(gè)單詞構(gòu)成,單詞之間使用空格隔開(kāi),單詞的類(lèi)型如下:
- 數(shù)字,例如 123
- 字母,例如 abc
- 操作符,例如 =、+、- 等符號(hào)
程序?qū)ξ谋痉治龊?,輸出單詞的類(lèi)型和值,假設(shè)輸入文本為 a = 123,則輸出如下:
alpha a
operator =
digit 123
程序的思路如下:
- 定義方法 parse_string,它將文本分割為多個(gè)單詞
- 定義方法 parse_word,它判斷并打印單詞的類(lèi)型
- 在方法 parse_string 中調(diào)用 parse_word 處理每個(gè)單詞
方法 parse_word 用于輔助實(shí)現(xiàn)方法 parse_string,不需要被外界訪問(wèn),因此將其設(shè)定為私有方法。
4.2 使用私有方法解決問(wèn)題
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,判斷單詞的類(lèi)型
- 在第 3 行,通過(guò)方法 isdigit 判斷是否為數(shù)字
- 在第 5 行, 通過(guò)方法 isalpha 判斷是否為字母
- 在第 10 行,定義公開(kāi)方法 parse_string
- 在第 11 行,使用 split 將文本分割為多個(gè)單詞
- 在第 13 行,循環(huán)調(diào)用私有方法 __parse_word 處理每個(gè)單詞
- 在第 16 行,在類(lèi) Parser 的外部,調(diào)用公開(kāi)方法 parse_string
實(shí)現(xiàn)方法 parse_string 是類(lèi) Parser 的接口,外界通過(guò)這個(gè)方法實(shí)現(xiàn)分析文本的功能;而方法 __parse_word 是一個(gè)輔助方法,它用于實(shí)現(xiàn)方法 parse_string,不需要公開(kāi)給外界調(diào)用,因此將它設(shè)定為私有方法。
程序運(yùn)行輸出如下:
alpha sum
operator =
alpha sum
operator +
digit 100