5.1 訪問層級
你應該注意到了構造函數有個奇怪的名字
__init__
?未來你會常常遇到這種情況。
在Python中,有不同的訪問層級來控制類別屬性和方法的可見性和安全性。主要的訪問控制機制包括在屬性或方法名前加上一或兩個底線(_
和__
)。
使用命名約定
單個底線 _
用來標示不應在類或模組外使用的屬性和方法。這不是強制的,但這是一個應該遵守的約定,以促進代碼的可讀性和維護性。
雙底線 __
用來標示真正需要私有和保護的屬性和方法,避免意外或故意的外部訪問。name mangling
機制使它們變得不那麼容易訪問,但仍然可以通過特定名稱訪問。
公共(public)
屬性和方法
公共屬性和方法
在代碼的任何位置都可訪問
。在Python中,預設所有屬性和方法都是公共的,除非它們的名字以底線開始。
class MyClass:
def __init__(self):
self.public_attribute = "I am public"
def public_method(self):
return "This is a public method"
obj = MyClass()
print(obj.public_attribute) # 可訪問
print(obj.public_method()) # 可訪問
在Python中,透過訪問層級實現了封裝,特別是通過非公共的屬性和方法。
5.2 非公共屬性和方法
受保護的(protected)
屬性和方法
受保護的屬性和方法以單個底線_
標示,並且設計為在類及其衍生類中使用。這是一個提醒程式員,數據不應在類外使用的約定。
class MyClass:
def __init__(self):
self._protected_attribute = "I am protected"
def _protected_method(self):
return "This is a protected method"
obj = MyClass()
print(obj._protected_attribute) # 可訪問,但不建議
print(obj._protected_method()) # 可訪問,但不建議
私有的(private)
屬性和方法
在Python中,私有屬性和方法以兩個底線__
標示,專供類內使用,主要目的是隱藏內部實現以及保護數據免受意外更改或外部使用。
為了防止外部代碼直接訪問這些屬性和方法,Python應用了特定的機制,稱為name mangling
(名稱混亂)。這個機制會自動修改私有屬性的名稱,為其加上由類名組成的前綴。因此,類MyClass
中的私有屬性__private_attribute
將轉換為內部名稱_MyClass__private_attribute
。
這可以防止數據被不小心訪問,同時保留了在類內工作的能力。然而,記住"name mangling"
機制並不是絕對的保護——有經驗的程式員可以使用修改過的名稱訪問這些數據。
讓我們來看看實際上是如何工作的:
class MyClass:
def __init__(self):
self.__private_attribute = "I am private"
def __private_method(self):
return "This is a private method"
def access_private_method(self):
return self.__private_method()
obj = MyClass()
# print(obj.__private_attribute) # 錯誤,無法直接訪問
# print(obj.__private_method()) # 錯誤,無法直接訪問
print(obj.access_private_method()) # 可通過類的公共方法訪問
正如例子所示,直接訪問私有屬性或方法會導致錯誤。但Python保留了通過修改後的名稱訪問的可能性。比如,你可以使用"混淆"的名稱訪問私有屬性,如下所示:
class MyClass:
def __init__(self):
self.__private_attribute = "I am private"
obj = MyClass()
print(obj._MyClass__private_attribute) # 顯示: I am private
雖然可以通過"混淆"的名稱訪問,但這應避免,因為這違反了封裝原則,可能導致代碼不穩定。
要看到Python如何修改屬性的名稱,你可以使用內建函數dir()
,它顯示對象的所有可用屬性和方法:
class MyClass:
def __init__(self):
self.__private_attribute = "I am private"
obj = MyClass()
print(dir(obj)) # 顯示對象的所有屬性和方法,包括"混淆"名稱
執行dir()
後,你會看到對象的所有屬性和方法列表,包括_MyClass__private_attribute
,這證實了"name mangling"
的機制。
5.3 自動調用方法
在使用構造函數時,有一個有趣的方面,你可能已經注意到了。方法__init__
會自動被調用。
事實上,這種情況還有很多,針對這些情況有很多的方法。例子包括:
方法__str__
如果你的對象有一個__str__
方法,它會在試圖將你的對象轉換為字符串時自動被調用,比如在使用print()
和str()
函數時。
class Cat:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name} is {self.age} years old"
cat = Cat("Barsik", 5)
print(cat) # 顯示: Barsik is 5 years old
方法__len__
如果你的對象有一個__len__
方法,則會在試圖確定對象的「長度」時自動被調用——被len()
函數使用。例子:
class MyList:
def __init__(self, items):
self.items = items
def __len__(self):
return len(self.items)
my_list = MyList([1, 2, 3])
print(len(my_list)) # 顯示: 3
這類「內建方法」你還會在將來遇到很多,使用起來非常享受,所以準備好:)
GO TO FULL VERSION