今天沒有新進度,只是寫code印證一下之前的「理論」。
先定義父類別:
class Tree():
_count = 0 # protected
def __init__(self, breed: str, age: int, height: int): # constructor
self.breed = breed # public
self._age = age # protected
self.__height = height # private
Tree._count += 1
def show_info(self):
print(f'{self.breed=:10}{self._age=:<10,}{self.__height=:<10,}')
@classmethod
def show_count(cls):
print(f'{cls._count=}')
上面這段code,有公開層級的breed
,保護層級的_age
和私有層級的__height
三個實例屬性,還有一個「保護」(註1)層級的類別屬性__count
。
子類別設一個就好。其中show_hardwood_count(cls)
的方法,加不加@classmethod裝飾器,似乎不會影響結果:
class Hardwood(Tree): # derived from Tree()
# 無自己的constructor。
# @classmethod # 不管加不加這個decorator都沒問題。
def show_hardwood_count(cls):
print(f'{cls._count = :<10}')
試從子類別取父類別的類別屬性_count
:
try:
hardwood = Hardwood('cedar', 1_000, 57)
hardwood.show_hardwood_count()
except Exception as e:
print(str(e))
結果順利取到資料:
這是當然的,因為類別屬性_count
只是保護層級。就算在C-like這掛真正有保護層級的物件導向語言,保護級的屬性,子類別都是存取得到的。況且Python的保護層級,只是「約定俗成」,語法上的效果就和公開層級無異。
父類別重新設計,將_count
改為私有層級的__count
:
class Tree():
__count = 0 # private
def __init__(self, breed: str, age: int, height: int): # constructor
self.breed = breed # public
self._age = age # protected
self.__height = height # private
Tree.__count += 1
def show_info(self):
print(f'{self.breed=:10}{self._age=:<10,}{self.__height=:<10,}')
@classmethod
def show_count(cls):
print(f'{cls.__count=}')
子類別設計隨著略加修改:
class Hardwood(Tree): # 繼承自Tree。
@classmethod
def show_hardwood_count(cls):
print(f'{cls.__count = :<10}') # 只改這行。
測試程式倒不必改,不過還是再貼一次比較清楚:
try:
hardwood = Hardwood('cedar', 1_000, 57)
hardwood.show_hardwood_count()
except Exception as e:
print(str(e))
結果拋出異常:
以上證明子類別無法存取父類別中私有層級的類別屬性。
再看實例屬性。父類別的設計和上面完全相同。為方便讀者閱讀,還是再貼一次:
class Tree():
__count = 0 # private
def __init__(self, breed: str, age: int, height: int): # constructor
self.breed = breed # public
self._age = age # protected
self.__height = height # private
Tree.__count += 1
def show_info(self):
print(f'{self.breed=:10}{self._age=:<10,}{self.__height=:<10,}')
@classmethod
def show_count(cls):
print(f'{cls.__count=}')
子類別則改為這樣:
class Hardwood(Tree): # 繼承自Tree。
def show_hardwood_info(self):
print(f'{self.breed=:10}{self._age=:<10,}')
print(f'{self.__height=}')
測試程式:建立一個Hardwood
型別的物件,看能否在子類別內部存取父類別的私有層級實例屬性:
try:
hardwood = Hardwood('cedar', 1_000, 57)
hardwood.show_hardwood_info()
except Exception as e:
print(str(e))
結果依然拋出異常:
小結:本日的「小哉問」子類別存取父類別的Private Attributes,真的不行嗎?
,答案是:Yes, 真的不行。
接下來要驗證子類別是否能呼叫父類別的私有方法:
class Tree():
def __init__(self, breed: str, age: int, height: int): # constructor
self.breed = breed
self._age = age
self.__height = height
def show_info(self): # public method
print(f'{self.breed=:10}{self._age=:<10,}{self.__height=:<10,}')
def _show_info(self): # protected method
print(f'{self.breed=:10}{self._age=:<10,}{self.__height=:<10,}')
def __show_info(self): # private method
print(f'{self.breed=:10}{self._age=:<10,}{self.__height=:<10,}')
class Hardwood(Tree): # Inherited from Tree
...
測試程式:
try:
hardwood = Hardwood('cedar', 3_560, 61)
hardwood.show_info() # fine
hardwood._show_info() # fine
hardwood.__show_info() # 這行會觸發exception。
except Exception as e:
print(str(e))
結果是:前兩行呼叫父類別hardwood.show_info()
(公開)、hardwood._show_info()
(保護)的方法都順利執行,而在呼叫父類別的私有方法hardwood.__show_info()
時,踢到了一塊大鐵板。
證明私有方法也不會傳給子孫。
註1: 別忘了筆者一再強調的:Python類別中保護層級的屬性和方法,只是大家約定的「默契」,並無實質「公權力」。