python 類的子類

看下面的代碼,請仔細閱讀,並看看是否能夠發現點什麼問題呢?

#!/usr/bin/env python
#coding:utf-8

class Person:
   def __init__(self, name, lang, email):
       self.name = name
       self.lang = lang
       self.email = email

   def author(self):
       return self.name

class Programmer:
   def __init__(self, name, lang, email, system, website):
       self.name = name
       self.lang = lang
       self.email = email
       self.system = system
       self.website = website

   def pythoner(self):
       pythoner_list = [ self.name, self.lang, self.email, self.system, self.website ]
       return pythoner_list

if __name__=="__main__":
   writer = Person("hiekay","Chinese","[email protected]")
   python = Programmer("hiekay","Python","[email protected]","Ubutun","hiekay.github.io")
   print "My name is:%s"%writer.author()
   print "I write program by:%s"%python.pythoner()[1]

上面這段代碼,運行起來沒有什麼問題,但是,仔細看,發現有兩個類,一個名字叫做Person,另外一個叫做Programmer,這還不是問題所在,問題所在是這兩個類的構造函數中,存在這相同的地方:self.name=name,self.lang=lang,self.email=email,這對於追求代碼質量的程序員,一般是不允許的。最好不要有重複代碼或者冗餘代碼。可是,在兩個類中都要有這些參數,應該怎麼辦呢?

子類、父類和繼承

看下面的代碼,裏面有兩個類A,B。這段程序能夠正確運行,每個類的功能是僅僅打印指定的內容。

#!/usr/bin/env python
#coding:utf-8

class A:
    def __init__(self):
        print "aaa"

class B:
    def __init__(self):
        print "bbb"

if __name__=="__main__":
    a = A()
    b = B()
 
#運行結果
aaa
bbb

上面的兩個類彼此之間沒有所謂的父子關係。現在稍加改變,將類B改寫,注意觀察與上面的差異。

#!/usr/bin/env python
#coding:utf-8

class A:
    def __init__(self):
        print "aaa"

class B(A):         #這裏和上面程序不同。B繼承了A
    def __init__(self):
        print "bbb"

if __name__=="__main__":
    a = A()
    b = B()

#運行結果
aaa
bbb

這段程序中,類B跟前面的那段有一點不同,class B(A):,這樣寫就表明了B相對A的關係:B是A的子類,B從A繼承A的所有東西(子承父業)。

但是,運行結果一樣。是的,那是以爲在B中儘管繼承了A,但是沒有調用任何A的東西,就好比兒子從老爸那裏繼承了財富,但是兒子一個子也沒動,外界看到的和沒有繼承一樣。

#!/usr/bin/env python
#coding:utf-8

class A:
    def __init__(self):
        print "aaa"

class B(A):
    def __init__(self):
        #print "bbb"
        A.__init__(self)    #運行繼承的父類

if __name__=="__main__":
    a = A()
    b = B()

#運行結果
aaa
aaa

這回運行結果有了變化,本來b=B()是運行類B,但是B繼承了A,並且在初始化的構造函數中,引入A的構造函數,所以,就運行A的結果相應結果了。

下面把最開頭的那端程序用子類繼承的方式重寫,可以是這樣的:

#!/usr/bin/env python
#coding:utf-8

class Person:
    def __init__(self, name, lang, email):
        self.name = name
        self.lang = lang
        self.email = email
    
    def author(self):
        return self.name
"""
class Programmer:
    def __init__(self, name, lang, email, system, website):
        self.name = name
        self.lang = lang
        self.email = email
        self.system = system
        self.website = website

    def pythoner(self):
        pythoner_list = [ self.name, self.lang, self.email, self.system, self.website ]
        return pythoner_list
"""

class Programmer(Person):       #繼承父類Person
    def __init__(self, name, lang, email, system, website):
        Person.__init__(self,name,lang,email)   #將Person.__init__()的功能繼承到這裏
        #self.name = name                       #這三句是Person中已經搞定的,就不用重複
        #self.lang = lang                       #通過繼承已經實現了這三句的功能
        #self.email = email
        self.system = system                    #子類中不同於Person父類部分
        self.website = website

    def pythoner(self):
        pythoner_list = [ self.name, self.lang, self.email, self.system, self.website ]
        return pythoner_list

if __name__=="__main__":
    writer = Person("hiekay","Chinese","[email protected]")
    python = Programmer("hiekay","Python","[email protected]","Ubutun","hiekay.github.io")
    print "My name is:%s"%writer.author()
    print "I write program by:%s"%python.pythoner()[1]

代碼運行結果與前面一樣。

需要提供注意的是,在子類中,如果要繼承父類,必須用顯明的方式將所繼承的父類方法寫出來,例如上面的Person.init(self,name,lang,email),必須這樣寫,才能算是在子類中進行了繼承。如果不寫上,是沒有繼承的。用編程江湖的黑話(比較文雅地稱爲“行話”)說就是“顯式調用父類方法”。

對於爲什麼要用繼承:

從技術上說,OOP裏,繼承最主要的用途是實現多態。對於多態而言,重要的是接口繼承性,屬性和行爲是否存在繼承性,這是不一定的。事實上,大量工程實踐表明,重度的行爲繼承會導致系統過度複雜和臃腫, 反而會降低靈活性。因此現在比較提倡的是基於接口的輕度繼承理念。這種模型裏因爲父類(接口類)完全沒有代碼,因此根本談不上什麼代碼複用了。

在Python裏,因爲存在Duck Type,接口定義的重要性大大的降低,繼承的作用也進一步的被削弱了。

另外,從邏輯上說,繼承的目的也不是爲了複用代碼,而是爲了理順關係。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章