Python 3.1 快速導覽 - 類別 方法改寫

子類別 (subclass) 可依本身特性設定自己的屬性 (attribute) 與方法 (method) ,也會從父類別 (superclass) 繼承 (inherit) 屬性與方法。一般來說,沒有設定成私有的屬性及方法都會被繼承,子類別可由父類別公開的方法存取父類別私有的屬性。



子類別也可依需要改寫 (override) 父類別的方法,這是說子類別需要用到與父類別具有相同名稱的方法,但是子類別需要的功能有所修改、擴充或增加,因此當子類別裡頭定義與父類別相同名稱的方法時,就會改寫父類別的方法。經過改寫,子類別的方法完全屬於子類別所有,例如以下程式
class Demo:
    __x = 0

    def __init__(self, i):
        self.__i = i
        Demo.__x += 1
    
    def __str__(self):
        return str(self.__i)
         
    def hello(self):
        print("hello " + self.__str__())
    
    @classmethod 
    def getX(cls):
        return cls.__x

class SubDemo(Demo):
    def __init__(self, i, j):
        self.__i = i
        self.__j = j
    
    def __str__(self):
        return str(self.__i) + "+" + str(self.__j)


a = SubDemo(1234)
a.hello()
print("a.__x =", a.getX())
b = SubDemo(56, 78)
b.hello()
print("b.__x =", b.getX())
print()
print("a.__x =", a.getX())
print("b.__x =", b.getX())

# 《程式語言教學誌》的範例程式
# http://pydoing.blogspot.com/
# 檔名:cla19.py
# 功能:示範 Python 程式 
# 作者:張凱慶
# 時間:西元 2010 年 12 月 


執行結果如下



Demo 為父類別,定義四個方法, SubDemo 為子類別,改寫 Demo 的兩個方法,包括 __init__() 與 __str__() 。


我們可以發現, Demo 有個 __x 變數,原本用來算有多少 Demo 實體 (instance) 被建立,我們想這應該要包括子類別 SubDemo 的實體數量,也就是說, __x 應該等於 Demo 實體總數加上 SubDemo 實體總數。此例中我們建立兩個 SubDemo 實體,可是 __x 卻等於 0 。


這是為什麼呢?因為此例中 Demo 的 __init__() 方法從頭到尾沒有被呼叫過,因此 __x 始終保持為初值 0 。解決這個問題的方法很簡單,一個幾單的途徑是在子類別 SubDemo 新增一個 __x ,但是這樣一來就只能累計 SubDemo 的實體數量,若是還有其他子類別繼承自 Demo ,這就無法一同列入計算了。


另一個解決途徑是在子類別改寫的方法中先呼叫 (call) 父類別的方法,這可利用內建函數 (function) super() ,如下
class Demo:
    __x = 0

    def __init__(self, i):
        self.__i = i
        Demo.__x += 1
    
    def __str__(self):
        return str(self.__i)
         
    def hello(self):
        print("hello " + self.__str__())
    
    @classmethod 
    def getX(cls):
        return cls.__x

class SubDemo(Demo):
    def __init__(self, i, j):
        super().__init__(i)
        self.__j = j
    
    def __str__(self):
        return super().__str__() + "+" + str(self.__j)


a = SubDemo(12, 34)
a.hello()
print("a.__x =", a.getX())
b = SubDemo(56, 78)
b.hello()
print("b.__x =", b.getX())
print()
print("a.__x =", a.getX())
print("b.__x =", b.getX())

# 《程式語言教學誌》的範例程式
# http://pydoing.blogspot.com/
# 檔名:cla20.py
# 功能:示範 Python 程式 
# 作者:張凱慶
# 時間:西元 2010 年 12 月 


執行結果如下



第 19 行到第 21 行
def __init__(self, i, j):
    super().__init__(i)
    self.__j = j


SubDemo 的 __init__() 定義中,我們利用 super() 呼叫父類別 Demo 的 __init__() ,因此需提供 i 當作參數 (parameter) 。注意,這裡 self.__i 變成父類別的私有屬性。


第 23 行到第 24 行
def __str__(self):
    return super().__str__() + "+" + str(self.__j)


這裡的 __str__() ,在 return 後的運算式 (expression) 先呼叫 super().__str__() ,因為 self.__i 已經變成父類別 Demo 私有屬性,因此需要先呼叫父類別的 __str__() 。


中英文術語對照
子類別subclass
屬性attribute
方法method
父類別superclass
繼承inherit
改寫override
實體instance
呼叫call
函數function
參數parameter
運算式expression






1 則留言:

Unknown 提到...

在第一個範例裡
a = SubDemo(1234)應該會報錯
因為找不到只吃一個引數的SubDemo建構子