Python之繼承


1、什么是繼承?

繼承指的是類與類之間的關系,是一種什么是什么的關系,功能之一就是用來解決代碼重用問題

繼承是一種創建新類的方式,在python中,新建的類可以繼承一個或多個父類,父類又可稱為基類或超類,新建的類稱為派生類或子類,繼承又fenwei單繼承和多繼承

class ParentClass1: #定義父類
    pass

class ParentClass2: #定義父類
    pass

class SubClass1(ParentClass1): #單繼承,基類是ParentClass1,派生類是SubClass
    pass

class SubClass2(ParentClass1,ParentClass2): #python支持多繼承,用逗號分隔開多個繼承的類
    pass

print(Son1.__bases__)  # 查看所有繼承的父類
print(Son2.__bases__)
===============
(<class '__main__.Father1'>,)
(<class '__main__.Father1'>, <class '__main__.Father2'>)
View Code

 

2、繼承與抽象

抽象分成兩個層次:

1.將奧巴馬和梅西這倆對象比較像的部分抽取成類;

2.將人,豬,狗這三個類比較像的部分抽取成父類。

抽象最主要的作用是划分類別(可以隔離關注點,降低復雜度)

clip_image001

繼承:

是基於抽象的結果,通過編程語言去實現它,肯定是先經歷抽象這個過程,才能通過繼承的方式去表達出抽象的結構。

抽象只是分析和設計的過程中,一個動作或者說一種技巧,通過抽象可以得到類

clip_image002

 

class animal():   # 定義父類
    country  =  'china'     # 這個叫類的變量
    def __init__(self,name,age):
        self.name=name   # 這些又叫數據屬性
        self.age=age

    def walk(self):         # 類的函數,方法,動態屬性
        print('%s is walking'%self.name)

    def say(self):
        pass

class people(animal):  # 子類繼承父類
    pass

class pig(animal):    # 子類繼承父類
    pass

class dog(animal):  # 子類繼承父類
    pass

aobama=people('aobama',60)   # 實例化一個對象
print(aobama.name)
aobama.walk()
===================
aobama
aobama is walking
View Code

 

3、派生

1.在父類的基礎上產生子類,產生的子類就叫做派生類

2.父類里沒有的方法,在子類中有了,這樣的方法就叫做派生方法。

3.父類里有,子類也有的方法,就叫做方法的重寫(就是把父類里的方法重寫了)

class Hero:
    def __init__(self, nickname,
                 aggressivity,
                 life_value):
        self.nickname = nickname
        self.aggressivity = aggressivity
        self.life_value = life_value

    def attack(self, enemy):
        enemy.life_value -= self.aggressivity

class Garen(Hero):   # 子類繼承  hero 父類
    camp='Demacia'   # 子類衍生出的變量
    def attack(self, enemy):   # 跟父類的 attack 重名,對象調用的時候以子類的為准
        pass
    def fire(self):    # 父類沒有 fire,這里 fire 屬於派生出來的東西
        print('%s is firing' %self.nickname)
class Riven(Hero):
    camp='Noxus'

g1=Garen('garen',18,200)
r1=Riven('rivren',18,200)
# print(g1.camp)
# print(r1.camp)
# g1.fire()
g1.attack(g1)
例1
class Hero:
    def __init__(self, nickname,aggressivity,life_value):
        self.nickname = nickname
        self.aggressivity = aggressivity
        self.life_value = life_value
    def attack(self, enemy):
        print('Hero attack')

class Garen(Hero):
    camp = 'Demacia'

    def attack(self, enemy): #self=g1,enemy=r1
        # self.attack(enemy) #g1.attack(r1),這里相當於無限遞歸
        Hero.attack(self,enemy)  # 引用 父類的 attack,對象會去跑 父類的 attack
        print('from garen attack')  # 再回來這里

    def fire(self):
        print('%s is firing' % self.nickname)

class Riven(Hero):
    camp = 'Noxus'


g1 = Garen('garen', 18, 200)
r1 = Riven('rivren', 18, 200)
g1.attack(r1)
# print(g1.camp)
# print(r1.camp)
# g1.fire()
例2

 

4、組合與重用性

重用性:

方式1:不通過繼承的方式重用屬性,指名道姓的使用哪個類的屬性

class Hero:
    def __init__(self,nickname,gongji,life):
        self.nickname=nickname
        self.gongji=gongji
        self.life=life

    def attack(self,obj):
        print('from Hero attack')

class Garen:
    def __init__(self,nickname,gongji,life,script):
        Hero.__init__(self,nickname,gongji,life)   # 這里引用Hero類的 init,不用再自己從新定義一遍 init
        self.script=script   # 父類 init 沒有 script,這里是新加進來的屬性

    def attack(self,obj):  # 在這里自己定義新的 attack,不再使用父類的 attack
        print('from Garen attack')

    def fire(self):  # 在這里定義新的功能
        print('from Garen fire')

g1=Garen('garen',18,200,'人在塔在')
print(g1.script)
人在塔在
View Code

提示:用已經有的類建立一個新的類,這樣就重用了已經有的軟件中的一部分甚至大部分,大大省了編程工作量,這就是常說的軟件重用,不僅可以重用自己的類,也可以繼承別人的,比如標准庫,來定制新的數據類型,這樣就是大大縮短了軟件開發周期,對大型軟件開發來說,意義重大.

注意:像g1.life之類的屬性引用,會先從實例中找life,然后去類中找,然后再去父類中找...直到最頂級的父類。

 

方式2:通過繼承

class Hero():
    def __init__(self, nickname, gongji, life):
        self.nickname = nickname
        self.gongji = gongji
        self.life = life

    def attack(self, obj):
        print('from Hero attack')
        obj.life -= self.gongji


class Garen(Hero):   # 使用 super方式需要繼承
    camp = 'Demacia'

    def __init__(self, nickname, gongji, life):
        super().__init__(nickname, gongji, life)

    def attack(self, obj):  # 在這里自己定義新的 attack,不再使用父類的 attack
        super(Garen, self).attack(obj)  # PY3中super可以不給參數,PY2中第一個參數必須是自己的類,self,可以使用父類的方法,方法需要給參數就給參數

    def fire(self):  # 在這里定義新的功能
        print('from Garen fire')


g1 = Garen('garen1', 18, 200)
g2 = Garen('garen2', 20, 100)
print(g2.life)
g1.attack(g2)
print(g2.life)

100
from Hero attack
82
例1
class A:
    def f1(self):
        print('from A')
        super().f1()    
        # 這種不需要繼承也可以使用到 super,為什么,要看 C的 MRO表


class B:
    def f1(self):
        print('from B')

class C(A,B):
    pass


print(C.mro())
#[<class '__main__.C'>,
# <class '__main__.A'>,
# <class '__main__.B'>, #  B在A的后面,當A指定 super().f1 會找到 B的 f1
# <class 'object'>]


c=C()
c.f1()
例2

 

 

組合:

軟件重用的重要方式除了繼承之外還有另外一種方式,即:組合

組合:一個對象的數據屬性是另一個對象,稱為組合

class Equip: #武器裝備類
    def fire(self):
        print('release Fire skill')

class Riven: #英雄Riven的類,一個英雄需要有裝備,因而需要組合Equip類
    camp='Noxus'
    def __init__(self,nickname):
        self.nickname=nickname
        self.equip=Equip() #用Equip類產生一個裝備,賦值給實例的equip屬性

r1=Riven('銳雯雯')
r1.equip.fire() #可以使用組合的類產生的對象所持有的方法

release Fire skill
View Code

組合的方式:

組合與繼承都是有效地利用已有類的資源的重要方式。但是二者的概念和使用場景皆不同

1.繼承的方式

通過繼承建立了派生類與基類之間的關系,它是一種'是'的關系,比如白馬是馬,人是動物。

當類之間有很多相同的功能,提取這些共同的功能做成基類,用繼承比較好,比如老師是人,學生是人

2.組合的方式

用組合的方式建立了類與組合的類之間的關系,它是一種‘有’的關系,比如教授有生日,教授教python和linux課程,教授有學生s1、s2、s3...

class People:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex

class Course:
    def __init__(self,name,period,price):
        self.name=name
        self.period=period
        self.price=price
    def tell_info(self):
        print('<%s %s %s>' %(self.name,self.period,self.price))

class Teacher(People):
    def __init__(self,name,age,sex,job_title):
        People.__init__(self,name,age,sex)
        self.job_title=job_title
        self.course=[]
        self.students=[]


class Student(People):
    def __init__(self,name,age,sex):
        People.__init__(self,name,age,sex)
        self.course=[]


egon=Teacher('egon',18,'male','沙河霸道金牌講師')
s1=Student('牛榴彈',18,'female')

python=Course('python','3mons',3000.0)
linux=Course('python','3mons',3000.0)

#為老師egon和學生s1添加課程
egon.course.append(python)
egon.course.append(linux)
s1.course.append(python)

#為老師egon添加學生s1
egon.students.append(s1)


#使用
for obj in egon.course:
    obj.tell_info()
View Code

 

5、接口與歸一化設計

a、為何要用接口?

接口提取了一群類共同的函數,可以把接口當做一個函數的集合。

然后讓子類去實現接口中的函數。

這么做的意義在於歸一化,什么叫歸一化,就是只要是基於同一個接口實現的類,那么所有的這些類產生的對象在使用時,從用法上來說都一樣。

歸一化的好處在於:

歸一化讓使用者無需關心對象的類是什么,只需要的知道這些對象都具備某些功能就可以了,這極大地降低了使用者的使用難度。

class Interface:#定義接口Interface類來模仿接口的概念,python中壓根就沒有interface關鍵字來定義一個接口。
    def read(self): #定接口函數read
        pass

    def write(self): #定義接口函數write
        pass


class Txt(Interface): #文本,具體實現read和write
    def read(self):
        print('文本數據的讀取方法')

    def write(self):
        print('文本數據的讀取方法')

class Sata(Interface): #磁盤,具體實現read和write
    def read(self):
        print('硬盤數據的讀取方法')

    def write(self):
        print('硬盤數據的讀取方法')

class Process(Interface):
    def read(self):
        print('進程數據的讀取方法')

    def write(self):
        print('進程數據的讀取方法')
View Code

上面的代碼只是看起來像接口,其實並沒有起到接口的作用,子類完全可以不用去實現接口,這就用到了抽象類

 

6、抽象類

子類必須繼承抽象類的方法,不然報錯

什么是抽象類?

與java一樣,python也有抽象類的概念但是同樣需要借助模塊實現,抽象類是一個特殊的類,它的特殊之處在於只能被繼承,不能被實例化

為什么要有抽象類?

如果說類是從一堆對象中抽取相同的內容而來的,那么抽象類就是從一堆類中抽取相同的內容而來的,內容包括數據屬性和函數屬性。

 比如我們有香蕉的類,有蘋果的類,有桃子的類,從這些類抽取相同的內容就是水果這個抽象的類,你吃水果時,要么是吃一個具體的香蕉,要么是吃一個具體的桃子。。。。。。你永遠無法吃到一個叫做水果的東西。

從設計角度去看,如果類是從現實對象抽象而來的,那么抽象類就是基於類抽象而來的。

  從實現角度來看,抽象類與普通類的不同之處在於:抽象類中只能有抽象方法(沒有實現功能),該類不能被實例化,只能被繼承,且子類必須實現抽象方法。

 

抽象類與接口

抽象類的本質還是類,指的是一組類的相似性,包括數據屬性(如all_type)和函數屬性(如read、write),而接口只強調函數屬性的相似性。

抽象類是一個介於類和接口直接的一個概念,同時具備類和接口的部分特性,可以用來實現歸一化設計

import abc
#抽象類:本質還是類,與普通類額外的特點的是:加了裝飾器的函數,子類必須實現他們
class Animal(metaclass=abc.ABCMeta):   # 抽象類是用來被子類繼承的,不是用來實例化的
    tag='123123123123123'
    @abc.abstractmethod   # 如果子類沒有我這個函數,主動拋出異常
    def run(self):
        pass
    @abc.abstractmethod
    def speak(self):
        pass



class People(Animal):
    def run(self):   # 子類必須有抽象類里的裝飾器下面的函數
        pass

    def speak(self):
        pass


peo1=People()   # 實例化出來一個人
print(peo1.tag)
例1
#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
#一切皆文件
import abc #利用abc模塊實現抽象類

class All_file(metaclass=abc.ABCMeta):
    all_type='file'
    @abc.abstractmethod #定義抽象方法,無需實現功能
    def read(self):
        '子類必須定義讀功能'
        pass

    @abc.abstractmethod #定義抽象方法,無需實現功能
    def write(self):
        '子類必須定義寫功能'
        pass

# class Txt(All_file):
#     pass
#
# t1=Txt() #報錯,子類沒有定義抽象方法

class Txt(All_file): #子類繼承抽象類,但是必須定義read和write方法
    def read(self):
        print('文本數據的讀取方法')

    def write(self):
        print('文本數據的讀取方法')

class Sata(All_file): #子類繼承抽象類,但是必須定義read和write方法
    def read(self):
        print('硬盤數據的讀取方法')

    def write(self):
        print('硬盤數據的讀取方法')

class Process(All_file): #子類繼承抽象類,但是必須定義read和write方法
    def read(self):
        print('進程數據的讀取方法')

    def write(self):
        print('進程數據的讀取方法')

wenbenwenjian=Txt()

yingpanwenjian=Sata()

jinchengwenjian=Process()

#這樣大家都是被歸一化了,也就是一切皆文件的思想
wenbenwenjian.read()
yingpanwenjian.write()
jinchengwenjian.read()

print(wenbenwenjian.all_type)
print(yingpanwenjian.all_type)
print(jinchengwenjian.all_type)
例2

.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM