Python Object-oriented Programming -5 Encapsulation
再談封裝
封裝(Encapsulation): 將資料與函數放在一種稱為物件的結構中
定義十分簡潔,事實上在前幾篇的例子中定義一個class也已經實作了封裝的動作。不過有些延伸的議題會產生,有時候設計時需要防止子類別或其他外部類別存取類別的屬性。
其它OOP程式語言如C++、Java,使用關鍵字protected, private, public不同屬性/方法的等級來實現此種可能需求。但在Python中,物件的所有屬性與方法都是public,這意味著防止任何屬性被呼叫是沒有意義的。
Python中,物件的所有屬性與方法都是public
雖然不能防止,但其實Python中仍有一些方式區別private。慣例上_var作為protected,__var作為private,以下來試驗:
class employee:
def __init__(self, name, sal):
self.name=name
self.__salary=salemp = Employee('Young', 500)
emp._name # Young
事實上,加上_
並沒有真正保護相關屬性,依然可以存取。但身為遵守慣例的工程師應該要知道:在Python中,_var
慣例上就是private,在物件之外不應修改其值(但做的到)。
其實透過變數命名__var
可以類似實現private的方式,讓試圖存取時出現錯誤:
class Employee:
def __init__(self, name, sal):
self.name=name
self.__salary=salemp = Employee('Young', 500)
emp.__salary# AttributeError: 'Employee' object has no attribute '__salary'
雖然觸發了AttributeError,看似不能存取該屬性,實際上是因為觸發了name mangling
。name mangling意義在於避免衍生類別/子類別名稱碰撞 (name collisions) ,這樣只是剛好觸發副作用而已。換言之,其實你硬要存取,可以透過_<class_name>__<attribute_name>
存取,即emp._Employee__salary
存取。
總結來說,一般靜態程式語言如Java,C++封裝中擁有的private, protected特性在Python中是介於有跟沒有之間的存在,官方文件如此開示:
“Private” instance variables that cannot be accessed except from inside an object don’t exist in Python. However, there is a convention that is followed by most Python code: a name prefixed with an underscore (e.g. _spam) should be treated as a non-public part of the API (whether it is a function, a method or a data member).
所以,其實private在Python中應該使用_
就好,並且注意到要自律地別任意存取。雖然沒任何強制的效果,但身為負責的工程師,好好遵循規範吧!如果需要追求類似private效果,或許你可以嘗試__
,但是如果懂的人,還是有法操作。