12. 面向?qū)ο螅ɡ^承/接口/抽象)

[TOC]

面向?qū)ο?/h1>

繼承與派生

繼承

  1. 什么是繼承?繼承是一種創(chuàng)建新的類(lèi)的方式
class A:
    pass
class B(A):
    pass

在python中,新建的類(lèi)可以繼承自一個(gè)或者多個(gè)父類(lèi),原始類(lèi)稱(chēng)為基類(lèi)或者超類(lèi),新建的類(lèi)稱(chēng)為派生類(lèi)或者子類(lèi)

python中類(lèi)的繼承分為,單繼承和多繼承。
查看繼承的方法B.__bases__

如果沒(méi)有指定基類(lèi),python的類(lèi)會(huì)默認(rèn)集成object類(lèi),object是所有python類(lèi)的基類(lèi)。

  1. 如何繼承-》如何尋找繼承關(guān)系

繼承是一種‘是’的關(guān)系:

人類(lèi)、豬類(lèi)、狗類(lèi)都繼承動(dòng)物類(lèi),因而他們都是動(dòng)物

抽象:抽取類(lèi)似或者說(shuō)比較相似的部分

老師是這樣定義這個(gè)抽象的,并且還介紹了一個(gè)抽象類(lèi)的概念abc,但是這個(gè)抽象的意思翻譯過(guò)來(lái)就是尋找對(duì)象的相似點(diǎn),將對(duì)象根據(jù)特征進(jìn)行歸納總結(jié)為類(lèi)的過(guò)程。
在找到相似點(diǎn)(也就是“抽象”)之后,其實(shí)對(duì)象往往還有細(xì)分部分的一些特征,并且會(huì)有層級(jí)地可以劃分類(lèi)別
例如:
動(dòng)物>>>
人/豬/狗>>>
奧巴馬/梅西 // 麥兜/豬八戒 // 史努比/丁丁

由此將對(duì)象原本模糊的關(guān)注點(diǎn)隔離開(kāi),降低復(fù)雜度

繼承:基于抽象的結(jié)果,通過(guò)編程語(yǔ)言去實(shí)現(xiàn),經(jīng)歷抽象的過(guò)程,通過(guò)繼承去表達(dá)出抽象的結(jié)構(gòu)

  1. 為什么要用繼承?
    解決代碼重用問(wèn)題:
    在了解了“繼承就是‘是’的關(guān)系”之后,我們會(huì)發(fā)現(xiàn),我們定義劃分開(kāi)的父子類(lèi)之間,其實(shí)是有些包含的、并且共有的功能。
    例如“人”這個(gè)類(lèi),都是可以“走”、“說(shuō)”的;動(dòng)物這個(gè)個(gè)類(lèi),都可以有“吃”的動(dòng)作。
    由此,我們就可以在父類(lèi)中定義共同有的方法、屬性,并且讓所有子類(lèi)都享用。
class hero:
    def __init__(self, name,aggressivity, life_value):
        self.name = name
        self.aggressivity = aggressivity
        self.life_value = life_value
    def attack(self,enemy):
        print('hero attack')

class garen(hero):
    def __init__(self,name,aggressivity,life_value,script):
        hero.__init__(self, name,aggressivity, life_value)
        self.script = script

這段代碼中,我們就定義了一個(gè)英雄類(lèi),他的子類(lèi)蓋倫可以繼承一些他定義的屬性
hero.__init__(self, name,aggressivity, life_value)
這就節(jié)約了蓋倫類(lèi)定義的代碼,并且給以后定義新的子類(lèi)英雄提供了便利。

但實(shí)際上,這種調(diào)用方法跟繼承并沒(méi)有關(guān)系,這種類(lèi)名.函數(shù)的調(diào)用方法其實(shí)可以調(diào)用所有其他類(lèi)的方法。而且當(dāng)子類(lèi)修改父類(lèi)之后,代碼還要隨之更改,非常麻煩,并不推薦這種方式。

這就是代碼的重用

當(dāng)然子類(lèi)也可以有自己的屬性,例如上面代碼中的self.script = script
如果說(shuō)子類(lèi)自己定義的屬性或者方法和父類(lèi)中有重名,那么就以子類(lèi)自己定義的為準(zhǔn)。

上面使用hero.__init__(self, name,aggressivity, life_value)就是在調(diào)用父類(lèi)函數(shù)的方法作為子類(lèi)定義函數(shù)的參數(shù),在使用父類(lèi)函數(shù)的時(shí)候,要記得不能再使用self,而是跟上要調(diào)用的類(lèi)的名稱(chēng)。

派生:
子類(lèi)繼承了父類(lèi)的屬性,然后衍生出自己新的屬性,
如果子類(lèi)衍生出的新的屬性與父類(lèi)的某個(gè)屬性名字相同,
那么再調(diào)用子類(lèi)的這個(gè)屬性,就以子類(lèi)自己這里的為準(zhǔn)了

繼承順序

class A(object):
    def test(self):
        print('from A')

class B(A):
    def test(self):
        print('from B')

class C(A):
    def test(self):
        print('from C')

class D(B):
    def test(self):
        print('from D')

class E(C):
    def test(self):
        print('from E')

class F(D,E):
    # def test(self):
    #     print('from F')
    pass
f1=F()
f1.test()
print(F.__mro__) #只有新式才有這個(gè)屬性可以查看線性列表,經(jīng)典類(lèi)沒(méi)有這個(gè)屬性

#新式類(lèi)繼承順序:F->D->B->E->C->A
#經(jīng)典類(lèi)繼承順序:F->D->B->A->E->C
#python3中統(tǒng)一都是新式類(lèi)
#pyhon2中才分新式類(lèi)與經(jīng)典類(lèi)

作業(yè)分析:

  • 新式類(lèi)的繼承順序總結(jié):
    C3算法非廣度優(yōu)先,利用MRO順序進(jìn)行繼承

    “不徹底”,“從左往右”
    向父類(lèi)遍歷,到達(dá)根類(lèi)的前一個(gè)類(lèi)就往另外的支線遍歷
  • 經(jīng)典類(lèi)中的集成順序總結(jié):
    深度優(yōu)先
    “徹底”,“從左往右”
    向父類(lèi)遍歷,到達(dá)根類(lèi)才向另外的支線遍歷

繼承原理

每定義一個(gè)淚,python會(huì)計(jì)算出一個(gè)方法解析順序(MRO)列表,這個(gè)MRO列表就是一個(gè)簡(jiǎn)單的所有基類(lèi)的線性順序列表:

PyObject* tp_mro
Tuple containing the expanded set of base types, starting with the type itself and
ending with object, in Method Resolution Order.
以元組的形式返回所有基類(lèi)的擴(kuò)展的集合,從他本身的類(lèi)開(kāi)始,到object類(lèi)結(jié)束
This field is not inherited; it is calculated fresh by PyType_Ready().
這個(gè)字段并不是通過(guò)繼承得到的,而是通過(guò)by PyType_Ready()方法計(jì)算更新的

>>> class tsr:
...     pass
...
>>> str.__mro__
(<class 'str'>, <class 'object'>)
>>> str.mro()
[<class 'str'>, <class 'object'>]

為了實(shí)現(xiàn)繼承,python會(huì)在MRO列表上從左到右開(kāi)始查找基類(lèi),直到找到第一個(gè)匹配這個(gè)屬性的類(lèi)為止。
而這個(gè)MRO列表的構(gòu)造是通過(guò)一個(gè)C3線性化算法來(lái)實(shí)現(xiàn)的。我們不去深究這個(gè)算法的數(shù)學(xué)原理,它實(shí)際上就是合并所有父類(lèi)的MRO列表并遵循如下三條準(zhǔn)則:

  1. 子類(lèi)會(huì)先于父類(lèi)被檢查
  2. 多個(gè)父類(lèi)會(huì)根據(jù)它們?cè)诹斜碇械捻樞虮粰z查
  3. 如果對(duì)下一個(gè)類(lèi)存在兩個(gè)合法的選擇,選擇第一個(gè)父類(lèi)

子類(lèi)調(diào)用父類(lèi)的方法(super)

方法一:
父類(lèi)名.父類(lèi)方法()

不多說(shuō),參考上面的引用

方法二:
super()
當(dāng)你使用super()函數(shù)時(shí),Python會(huì)在MRO列表上繼續(xù)搜索下一個(gè)類(lèi)。只要每個(gè)重定義的方法統(tǒng)一使用super()并只調(diào)用它一次,那么控制流最終會(huì)遍歷完整個(gè)MRO列表,每個(gè)方法也只會(huì)被調(diào)用一次(注意注意注意:使用super調(diào)用的所有屬性,都是從MRO列表當(dāng)前的位置往后找,千萬(wàn)不要通過(guò)看代碼去找繼承關(guān)系,一定要看MRO列表
思考題:


上圖的代碼以及運(yùn)行結(jié)果已經(jīng)放在圖中,下面的代碼使用的是糾正使用super()的代碼:

class A(object):
    def __init__(self):
        print("enter A")
        super(A, self).__init__()  # new
        print("leave A")

class B(object):
    def __init__(self):
        print("enter B")
        super(B, self).__init__()  # new
        print("leave B")

class C(A):
    def __init__(self):
        print("enter C")
        super(C, self).__init__()
        print("leave C")

class D(A):
    def __init__(self):
        print("enter D")
        super(D, self).__init__()
        print("leave D")
class E(B, C):
    def __init__(self):
        print("enter E")
        super(E, self).__init__()  # change
        print("leave E")

class F(E, D):
    def __init__(self):
        print("enter F")
        super(F, self).__init__()  # change
        print ("leave F")

f = F()

小結(jié):

  1. super并不是一個(gè)函數(shù),是一個(gè)類(lèi)名,形如super(B, self)事實(shí)上調(diào)用了super類(lèi)的初始化函數(shù),產(chǎn)生了一個(gè)super對(duì)象;
  2. super類(lèi)的初始化函數(shù)并沒(méi)有做什么特殊的操作,只是簡(jiǎn)單記錄了類(lèi)類(lèi)型和具體實(shí)例;
  3. super(B, self).func的調(diào)用并不是用于調(diào)用當(dāng)前類(lèi)的父類(lèi)的func函數(shù);
  4. Python的多繼承類(lèi)是通過(guò)mro的方式來(lái)保證各個(gè)父類(lèi)的函數(shù)被逐一調(diào)用,而且保證每個(gè)父類(lèi)函數(shù)只調(diào)用一次(如果每個(gè)類(lèi)都使用super);
  5. 混用super類(lèi)和非綁定的函數(shù)是一個(gè)危險(xiǎn)行為,這可能導(dǎo)致應(yīng)該調(diào)用的父類(lèi)函數(shù)沒(méi)有調(diào)用或者一個(gè)父類(lèi)函數(shù)被調(diào)用多次。

組合

組合對(duì)比繼承來(lái)說(shuō),也是用來(lái)重用代碼,但是組合描述的是一種“有”的關(guān)系

老師有課程
學(xué)生有成績(jī)
學(xué)生有課程
學(xué)校有老師
學(xué)校有學(xué)生

#定義課程類(lèi)
class Course:
    def __init__(self,name,price,period):
        self.name=name
        self.price=price
        self.period=period

#定義老師類(lèi)
class Teacher:
    def __init__(name,course):
        self.name=name
        self.course=course
#定義學(xué)生類(lèi)
class Student:
    def __init__(name,course):
        self.name=name
        self.course=course
#學(xué)生類(lèi)和老師類(lèi)中,都有關(guān)聯(lián)到課程這個(gè)屬性
#所以實(shí)例化一項(xiàng)課程
python=Course('python',15800,'7m')
#在實(shí)例化學(xué)生和老師的時(shí)候, 將實(shí)例化后的課程作為參數(shù)傳入
t1=Teacher('egon',python)
s1=Student('alex',python)

#這樣學(xué)生和老師就獲得了課程實(shí)例的參數(shù),并且可以引用
print(s1.course.name)
print(s1.course.period)

上述代碼就表示了一個(gè)“組合”的過(guò)程。類(lèi)之間通過(guò)實(shí)例化傳參的過(guò)程,來(lái)互相獲取一些需要的數(shù)據(jù),并且建立關(guān)系。

接口

繼承有兩種用途:
一:繼承基類(lèi)的方法,并且做出自己的改變或者擴(kuò)展(代碼重用)
二:聲明某個(gè)子類(lèi)兼容于某基類(lèi),定義一個(gè)接口類(lèi)Interface,接口類(lèi)中定義了一些接口名(就是函數(shù)名)且并未實(shí)現(xiàn)接口的功能,子類(lèi)繼承接口類(lèi),并且實(shí)現(xiàn)接口中的功能

接口的概念:

=================第一部分:Java 語(yǔ)言中的接口很好的展現(xiàn)了接口的含義: IAnimal.java
/*
* Java的Interface很好的體現(xiàn)了我們前面分析的接口的特征:
* 1)是一組功能的集合,而不是一個(gè)功能
* 2)接口的功能用于交互,所有的功能都是public,即別的對(duì)象可操作
* 3)接口只定義函數(shù),但不涉及函數(shù)實(shí)現(xiàn)
* 4)這些功能是相關(guān)的,都是動(dòng)物相關(guān)的功能,但光合作用就不適宜放到IAnimal里面了 */

package com.oo.demo;
public interface IAnimal {
    public void eat();
    public void run(); 
    public void sleep(); 
    public void speak();
}

=================第二部分:Pig.java:豬”的類(lèi)設(shè)計(jì),實(shí)現(xiàn)了IAnnimal接口 
package com.oo.demo;
public class Pig implements IAnimal{ //如下每個(gè)函數(shù)都需要詳細(xì)實(shí)現(xiàn)
    public void eat(){
        System.out.println("Pig like to eat grass");
    }

    public void run(){
        System.out.println("Pig run: front legs, back legs");
    }

    public void sleep(){
        System.out.println("Pig sleep 16 hours every day");
    }

    public void speak(){
        System.out.println("Pig can not speak"); }
}

=================第三部分:Person2.java
/*
*實(shí)現(xiàn)了IAnimal的“人”,有幾點(diǎn)說(shuō)明一下: 
* 1)同樣都實(shí)現(xiàn)了IAnimal的接口,但“人”和“豬”的實(shí)現(xiàn)不一樣,為了避免太多代碼導(dǎo)致影響閱讀,這里的代碼簡(jiǎn)化成一行,但輸出的內(nèi)容不一樣,實(shí)際項(xiàng)目中同一接口的同一功能點(diǎn),不同的類(lèi)實(shí)現(xiàn)完全不一樣
* 2)這里同樣是“人”這個(gè)類(lèi),但和前面介紹類(lèi)時(shí)給的類(lèi)“Person”完全不一樣,這是因?yàn)橥瑯拥倪壿嫺拍?在不同的應(yīng)用場(chǎng)景下,具備的屬性和功能是完全不一樣的 */

package com.oo.demo;
public class Person2 implements IAnimal { 
    public void eat(){
        System.out.println("Person like to eat meat");
    }

    public void run(){
        System.out.println("Person run: left leg, right leg");
    }

    public void sleep(){
        System.out.println("Person sleep 8 hours every dat"); 
    }

    public void speak(){
        System.out.println("Hellow world, I am a person");
    }
}

=================第四部分:Tester03.java
package com.oo.demo;

public class Tester03 {
    public static void main(String[] args) {
        System.out.println("===This is a person==="); 
        IAnimal person = new Person2();
        person.eat();
        person.run();
        person.sleep();
        person.speak();

        System.out.println("\n===This is a pig===");
        IAnimal pig = new Pig();
        pig.eat();
        pig.run();
        pig.sleep();
        pig.speak();
    }
}

java中的interface

接口的概念解釋和使用:

這里有一篇解釋接口的文章,其中關(guān)于什么時(shí)候使用繼承,什么時(shí)候用(組合)接口的方面,我看的模模糊糊。但是說(shuō)不定以后能看懂:http://blog.csdn.net/xiaoxiongli/article/details/2791853
其中有一段解釋說(shuō)的很好,用來(lái)描述接口的概念真的妙到毫顫,特意摘下來(lái)整理,留作以后裝逼用:

一馬平川 19:58:54

接口是對(duì)類(lèi)的抽象,我如果直接跟你說(shuō)接口編程,你一定不理解,或者說(shuō)很難理解,因?yàn)榻涌诒旧硎呛艹橄蟮臇|西,現(xiàn)在我舉例跟你說(shuō):

“電源插座就是接口”:

比方說(shuō),插座有兩孔的,有三孔的,不同的插頭需要不同的插座。
接口就描述了能適應(yīng)的插頭范圍現(xiàn)在有一種插座是三孔的,但既可以插三孔的,也可插兩孔的,知道么?
那么,我們可以說(shuō),這個(gè)插座設(shè)計(jì)的好,因?yàn)樗苓m用更廣的范圍。
當(dāng)然,這個(gè)范圍不能超出電源插座這個(gè)概念。
如果是用來(lái)插筆,做筆筒用,那也不適合。
如果電源插座不但能適用兩孔和三孔的插頭,還能適用筆的話,那么我們可以肯定的說(shuō),這個(gè)接口設(shè)計(jì)的太差了。因?yàn)榻涌冢ú遄┑脑O(shè)計(jì)應(yīng)該是對(duì)某一類(lèi)事物的抽象。而且,接口(插座)實(shí)現(xiàn)以后,實(shí)現(xiàn)該接口的類(lèi)(插頭)必須符合接口的定義(插座和插口匹配),而且需要完全符合,一點(diǎn)不符合都不行。

所以實(shí)現(xiàn)某個(gè)接口的類(lèi),必須重寫(xiě)接口中定義的所有方法。如果你覺(jué)得該方法不需要實(shí)現(xiàn),你可以留空,但必須重寫(xiě)。

看我這句話:“接口只定義了方法的原型,即參數(shù)和方法名以及返回值,集成接口的類(lèi)需要實(shí)現(xiàn)它。”

而且,接口(插座)實(shí)現(xiàn)以后,實(shí)現(xiàn)該接口的類(lèi)(插頭)必須符合接口的定義(插座和插口匹配)。其實(shí),你會(huì)發(fā)現(xiàn)插座生產(chǎn)出來(lái)后,如果某電器的插頭和插座不匹配,那么就無(wú)法使用該電器了。實(shí)際上,你在設(shè)計(jì)一個(gè)接口的時(shí)候,很難想到要怎么去設(shè)計(jì),盡管你知道集成這個(gè)接口的類(lèi)是怎么樣的。就像如果你開(kāi)一個(gè)工廠生產(chǎn)插線板,你在不知道電器,或不完全知道電器的插頭如何設(shè)計(jì)的時(shí)候,你是很難生產(chǎn)出能用的插線板的。

那么,如何設(shè)計(jì)插線板呢?或者說(shuō)如何設(shè)計(jì)接口呢?

先看看插線板廠商是如何生產(chǎn)的吧。

某天,有人生產(chǎn)一個(gè)電器是4個(gè)孔的,那就用不了了。這時(shí)候,插線板廠商為了生產(chǎn)出一種插線板,能適用于目前的大部分電器,也能適用于將來(lái)的電器,他找到了一個(gè)機(jī)構(gòu)。機(jī)構(gòu)是專(zhuān)門(mén)指定規(guī)則的,專(zhuān)門(mén)制定協(xié)議的。機(jī)構(gòu)叫來(lái)了大部分的重要電器廠商的頭頭,和插線板老板一起開(kāi)了個(gè)會(huì)。大家為了共同的利益,決定了一份協(xié)議。
協(xié)議是這樣的:電器廠商以后生產(chǎn)的電器的插頭,只能生產(chǎn)三孔的,但為了兼容目前市場(chǎng)上已有的電器,也能生產(chǎn)兩孔的,但是盡量生產(chǎn)三孔的。而且孔的大小和之間的距離有明確的規(guī)定。插線板廠商的插線板也只能有兩孔的和三孔的,而且孔的大小和之間的距離也必須按照協(xié)議來(lái)生產(chǎn)。
于是問(wèn)題解決了,而且插線板廠商老板很聰明,他發(fā)現(xiàn)可以生產(chǎn)出既可以插兩孔,又可以插三孔的插口,于是他的插線板大賣(mài),他發(fā)財(cái)了。優(yōu)秀的接口設(shè)計(jì),給他帶來(lái)了大大的好處,但他很聰明,他沒(méi)忘記如果沒(méi)有規(guī)范協(xié)議的機(jī)構(gòu),一切都是空白。

再補(bǔ)充幾句吧,不然你還是難以理解。

當(dāng)你想設(shè)計(jì)一個(gè)接口的時(shí)候,你最好先寫(xiě)幾個(gè)將要繼承這個(gè)幾口的類(lèi),寫(xiě)幾個(gè)只有框架而無(wú)實(shí)際內(nèi)容的類(lèi),看看他們之間的共性,找到寫(xiě)接口的點(diǎn),這就正如找電器老板來(lái)開(kāi)會(huì)。寫(xiě)接口的時(shí)候,你需要在之前對(duì)接口進(jìn)行說(shuō)明,說(shuō)明接口的適用范圍,以及繼承該接口的注意事項(xiàng),這就好比請(qǐng)機(jī)構(gòu)來(lái)制定協(xié)議規(guī)范。有了這些以后,你的接口在被使用的時(shí)候就不會(huì)錯(cuò)用,在寫(xiě)繼承該接口的類(lèi)的時(shí)候,也會(huì)按照規(guī)范完全的匹配接口。

最后一句話,即使你理解了我今晚所講的每一句話,你還是不會(huì)寫(xiě)接口,因?yàn)槟阈枰獙?shí)踐,實(shí)踐才會(huì)出真知。最后這句話才是至理名言,我說(shuō)的基本都是空話(在你學(xué)會(huì)了寫(xiě)接口后)。


第一次寫(xiě)接口時(shí),第一個(gè)感覺(jué)就是,寫(xiě)接口跟沒(méi)寫(xiě)一樣。定義一個(gè)接口,馬上去寫(xiě)實(shí)現(xiàn)類(lèi)!其實(shí)此時(shí)就是用著面向過(guò)程的思路寫(xiě)程序,然后掛了個(gè)羊頭,說(shuō)起來(lái)怎么也有個(gè)接口了!

今天看了一位老兄寫(xiě)的對(duì)于接口的心得體會(huì),真是太有同感了!

不要為了接口而接口,當(dāng)你把自己不當(dāng)做是個(gè)程序員來(lái)思考時(shí),就能把用人的思想來(lái)思考了,你不會(huì)寫(xiě)程序,就不會(huì)考慮細(xì)節(jié)的實(shí)現(xiàn)了!此時(shí)你所關(guān)注的問(wèn)題就是比較抽象的了,你看這不正符合面向?qū)ο蟮脑瓌t嗎?當(dāng)年張三豐教張無(wú)忌打太極就是要把招式全忘了,你要定義接口前就先忘了自己是個(gè)程序員吧!

當(dāng)然不可能有100%的抽象,最終你還是要回到實(shí)現(xiàn)細(xì)節(jié)上來(lái)的,可此時(shí)你已是學(xué)會(huì)了太極的張無(wú)忌了!

python中的接口:

單純?yōu)榱舜a重用的繼承,在實(shí)際的使用中很容易造成“高耦合”的問(wèn)題。
而接口繼承,根據(jù)接口的概念“給使用接口的對(duì)象一個(gè)很好的抽象”,將對(duì)象的一些特征進(jìn)行歸一化處理,使外部調(diào)用者無(wú)需關(guān)心細(xì)節(jié),可以一視同仁地處理特定接口的所有對(duì)象。

但是在python中沒(méi)有interface的概念。在python中定義接口,僅僅是看起來(lái)像接口:

第一次寫(xiě)接口時(shí),第一個(gè)感覺(jué)就是,寫(xiě)接口跟沒(méi)寫(xiě)一樣。定義一個(gè)接口,馬上去寫(xiě)實(shí)現(xiàn)類(lèi)!其實(shí)此時(shí)就是用著面向過(guò)程的思路寫(xiě)程序,然后掛了個(gè)羊頭,說(shuō)起來(lái)怎么也有個(gè)接口了!

接口提取了一群類(lèi)共同的函數(shù),可以把接口當(dāng)做一個(gè)函數(shù)的集合。

然后讓子類(lèi)去實(shí)現(xiàn)接口中的函數(shù)。

這么做的意義在于歸一化,什么叫歸一化,就是只要是基于同一個(gè)接口實(shí)現(xiàn)的類(lèi),那么所有的這些類(lèi)產(chǎn)生的對(duì)象在使用時(shí),從用法上來(lái)說(shuō)都一樣。

歸一化,讓使用者無(wú)需關(guān)心對(duì)象的類(lèi)是什么,只需要的知道這些對(duì)象都具備某些功能就可以了,這極大地降低了使用者的使用難度。

抽象類(lèi)

  • 定義:
    抽象類(lèi)是一個(gè)特殊的類(lèi),他的特殊之處在于:只能被繼承,不能被實(shí)例化

  • 為什么要有抽象類(lèi)
    類(lèi)是對(duì)相似的對(duì)象進(jìn)行一個(gè)總結(jié)
    抽象類(lèi)就是對(duì)有共同點(diǎn)的類(lèi)的一種抽取總結(jié)(不完全說(shuō)是總結(jié),是因?yàn)轭?lèi)和類(lèi)之間一定存在不同,不能完全統(tǒng)一總結(jié)成一類(lèi),真的是總結(jié)關(guān)系的話,應(yīng)該就是子類(lèi)和父類(lèi)的關(guān)系了)。

從實(shí)現(xiàn)的角度來(lái)看,抽象類(lèi)與普通類(lèi)的不同在于:
抽象類(lèi)只能有抽象方法(沒(méi)有實(shí)現(xiàn)功能),這類(lèi)不能被實(shí)例化,只能被繼承,而且子類(lèi)必須實(shí)現(xiàn)方法
從以上描述來(lái)看,抽象類(lèi)非常類(lèi)似接口,但是仍然是有差別的。

#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
#一切皆文件
import abc #利用abc模塊實(shí)現(xiàn)抽象類(lèi)

class All_file(metaclass=abc.ABCMeta):
    all_type='file'
    @abc.abstractmethod #定義抽象方法,無(wú)需實(shí)現(xiàn)功能
    def read(self):
        '子類(lèi)必須定義讀功能'    #如果子類(lèi)不實(shí)現(xiàn),就會(huì)報(bào)出此錯(cuò)誤,以此來(lái)實(shí)現(xiàn)類(lèi)似接口的功能
        pass

    @abc.abstractmethod #定義抽象方法,無(wú)需實(shí)現(xiàn)功能
    def write(self):
        '子類(lèi)必須定義寫(xiě)功能'
        pass

# class Txt(All_file):
#     pass
#
# t1=Txt() #報(bào)錯(cuò),子類(lèi)沒(méi)有定義抽象方法

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

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

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

    def write(self):
        print('硬盤(pán)數(shù)據(jù)的讀取方法')

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

    def write(self):
        print('進(jìn)程數(shù)據(jù)的讀取方法')

wenbenwenjian=Txt()

yingpanwenjian=Sata()

jinchengwenjian=Process()

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

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

由上面的代碼可以看出,抽象類(lèi)基本上實(shí)現(xiàn)了接口的功能,但是抽象類(lèi)的本質(zhì)還是一個(gè)類(lèi)。指的是一組淚的相似性,包括數(shù)據(jù)屬性和函數(shù)屬性,而接口只強(qiáng)調(diào)函數(shù)屬性的相似性。
所以,抽象類(lèi)是一個(gè)介于類(lèi)和接口之ijede一個(gè)概念,同時(shí)具備類(lèi)和接口的部分特性,可以用來(lái)實(shí)現(xiàn)歸一化設(shè)計(jì)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,071評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,409評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,569評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,360評(píng)論 6 404
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,895評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,123評(píng)論 0 286
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,643評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,559評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,742評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評(píng)論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,981評(píng)論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,363評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,622評(píng)論 1 280
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,354評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,707評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容