面向对象
继承与派生
继承
继承顺序
继承原理
子类调用父类的方法(super)
组合
接口
接口的概念:
接口的概念解释和使用:
python中的接口:
抽象类

面向对象

继承与派生

继承

  1. 什么是继承?继承是一种创建新的类的方式
class A:pass
class B(A):pass

在python中,新建的类可以继承自一个或者多个父类,原始类称为基类或者超类,新建的类称为派生类或者子类

python中类的继承分为,单继承和多继承。
查看继承的方法B.__bases__

如果没有指定基类,python的类会默认集成object类,object是所有python类的基类。

  1. 如何继承-》如何寻找继承关系

继承是一种‘是’的关系:

人类、猪类、狗类都继承动物类,因而他们都是动物

抽象:抽取类似或者说比较相似的部分

老师是这样定义这个抽象的,并且还介绍了一个抽象类的概念abc,但是这个抽象的意思翻译过来就是寻找对象的相似点,将对象根据特征进行归纳总结为类的过程。
在找到相似点(也就是“抽象”)之后,其实对象往往还有细分部分的一些特征,并且会有层级地可以划分类别
例如:
动物>>>
人/猪/狗>>>
奥巴马/梅西 // 麦兜/猪八戒 // 史努比/丁丁

由此将对象原本模糊的关注点隔离开,降低复杂度

继承:基于抽象的结果,通过编程语言去实现,经历抽象的过程,通过继承去表达出抽象的结构

  1. 为什么要用继承?
    解决代码重用问题:
    在了解了“继承就是‘是’的关系”之后,我们会发现,我们定义划分开的父子类之间,其实是有些包含的、并且共有的功能。
    例如“人”这个类,都是可以“走”、“说”的;动物这个个类,都可以有“吃”的动作。
    由此,我们就可以在父类中定义共同有的方法、属性,并且让所有子类都享用。
class hero:def __init__(self, name,aggressivity, life_value):self.name = nameself.aggressivity = aggressivityself.life_value = life_valuedef 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

这段代码中,我们就定义了一个英雄类,他的子类盖伦可以继承一些他定义的属性
hero.__init__(self, name,aggressivity, life_value)
这就节约了盖伦类定义的代码,并且给以后定义新的子类英雄提供了便利。

但实际上,这种调用方法跟继承并没有关系,这种类名.函数的调用方法其实可以调用所有其他类的方法。而且当子类修改父类之后,代码还要随之更改,非常麻烦,并不推荐这种方式。

这就是代码的重用

当然子类也可以有自己的属性,例如上面代码中的self.script = script
如果说子类自己定义的属性或者方法和父类中有重名,那么就以子类自己定义的为准。

上面使用hero.__init__(self, name,aggressivity, life_value)就是在调用父类函数的方法作为子类定义函数的参数,在使用父类函数的时候,要记得不能再使用self,而是跟上要调用的类的名称。

派生:
子类继承了父类的属性,然后衍生出自己新的属性,
如果子类衍生出的新的属性与父类的某个属性名字相同,
那么再调用子类的这个属性,就以子类自己这里的为准了

继承顺序

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__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性#新式类继承顺序:F->D->B->E->C->A
#经典类继承顺序:F->D->B->A->E->C
#python3中统一都是新式类
#pyhon2中才分新式类与经典类

作业分析:

  • 新式类的继承顺序总结:
    C3算法非广度优先,利用MRO顺序进行继承

    “不彻底”,“从左往右”
    向父类遍历,到达根类的前一个类就往另外的支线遍历
  • 经典类中的集成顺序总结:
    深度优先
    “彻底”,“从左往右”
    向父类遍历,到达根类才向另外的支线遍历

继承原理

每定义一个泪,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表:

PyObject* tp_mro
Tuple containing the expanded set of base types, starting with the type itself and
ending with object, in Method Resolution Order.
以元组的形式返回所有基类的扩展的集合,从他本身的类开始,到object类结束
This field is not inherited; it is calculated fresh by PyType_Ready().
这个字段并不是通过继承得到的,而是通过by PyType_Ready()方法计算更新的

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

为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:

  1. 子类会先于父类被检查
  2. 多个父类会根据它们在列表中的顺序被检查
  3. 如果对下一个类存在两个合法的选择,选择第一个父类

子类调用父类的方法(super)

方法一:
父类名.父类方法()

不多说,参考上面的引用

方法二:
super()
当你使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次(注意注意注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表
思考题:

上图的代码以及运行结果已经放在图中,下面的代码使用的是纠正使用super()的代码:

class A(object):def __init__(self):print("enter A")super(A, self).__init__()  # newprint("leave A")class B(object):def __init__(self):print("enter B")super(B, self).__init__()  # newprint("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__()  # changeprint("leave E")class F(E, D):def __init__(self):print("enter F")super(F, self).__init__()  # changeprint ("leave F")f = F()

小结:

  1. super并不是一个函数,是一个类名,形如super(B, self)事实上调用了super类的初始化函数,产生了一个super对象;
  2. super类的初始化函数并没有做什么特殊的操作,只是简单记录了类类型和具体实例;
  3. super(B, self).func的调用并不是用于调用当前类的父类的func函数;
  4. Python的多继承类是通过mro的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数只调用一次(如果每个类都使用super);
  5. 混用super类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一个父类函数被调用多次。

组合

组合对比继承来说,也是用来重用代码,但是组合描述的是一种“有”的关系

老师有课程
学生有成绩
学生有课程
学校有老师
学校有学生

#定义课程类
class Course:def __init__(self,name,price,period):self.name=nameself.price=priceself.period=period#定义老师类
class Teacher:def __init__(name,course):self.name=nameself.course=course
#定义学生类
class Student:def __init__(name,course):self.name=nameself.course=course
#学生类和老师类中,都有关联到课程这个属性
#所以实例化一项课程
python=Course('python',15800,'7m')
#在实例化学生和老师的时候, 将实例化后的课程作为参数传入
t1=Teacher('egon',python)
s1=Student('alex',python)#这样学生和老师就获得了课程实例的参数,并且可以引用
print(s1.course.name)
print(s1.course.period)

上述代码就表示了一个“组合”的过程。类之间通过实例化传参的过程,来互相获取一些需要的数据,并且建立关系。

接口

继承有两种用途:
一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能

接口的概念:

=================第一部分:Java 语言中的接口很好的展现了接口的含义: IAnimal.java
/*
* Java的Interface很好的体现了我们前面分析的接口的特征:
* 1)是一组功能的集合,而不是一个功能
* 2)接口的功能用于交互,所有的功能都是public,即别的对象可操作
* 3)接口只定义函数,但不涉及函数实现
* 4)这些功能是相关的,都是动物相关的功能,但光合作用就不适宜放到IAnimal里面了 */package com.oo.demo;
public interface IAnimal {public void eat();public void run(); public void sleep(); public void speak();
}=================第二部分:Pig.java:猪”的类设计,实现了IAnnimal接口
package com.oo.demo;
public class Pig implements IAnimal{ //如下每个函数都需要详细实现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
/*
*实现了IAnimal的“人”,有几点说明一下:
* 1)同样都实现了IAnimal的接口,但“人”和“猪”的实现不一样,为了避免太多代码导致影响阅读,这里的代码简化成一行,但输出的内容不一样,实际项目中同一接口的同一功能点,不同的类实现完全不一样
* 2)这里同样是“人”这个类,但和前面介绍类时给的类“Person”完全不一样,这是因为同样的逻辑概念,在不同的应用场景下,具备的属性和功能是完全不一样的 */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

接口的概念解释和使用:

这里有一篇解释接口的文章,其中关于什么时候使用继承,什么时候用(组合)接口的方面,我看的模模糊糊。但是说不定以后能看懂:http://blog.csdn.net/xiaoxiongli/article/details/2791853
其中有一段解释说的很好,用来描述接口的概念真的妙到毫颤,特意摘下来整理,留作以后装逼用:

一马平川 19:58:54

接口是对类的抽象,我如果直接跟你说接口编程,你一定不理解,或者说很难理解,因为接口本身是很抽象的东西,现在我举例跟你说:

“电源插座就是接口”:

比方说,插座有两孔的,有三孔的,不同的插头需要不同的插座。
接口就描述了能适应的插头范围现在有一种插座是三孔的,但既可以插三孔的,也可插两孔的,知道么?
那么,我们可以说,这个插座设计的好,因为他能适用更广的范围。
当然,这个范围不能超出电源插座这个概念。
如果是用来插笔,做笔筒用,那也不适合。
如果电源插座不但能适用两孔和三孔的插头,还能适用笔的话,那么我们可以肯定的说,这个接口设计的太差了。因为接口(插座)的设计应该是对某一类事物的抽象。而且,接口(插座)实现以后,实现该接口的类(插头)必须符合接口的定义(插座和插口匹配),而且需要完全符合,一点不符合都不行。

所以实现某个接口的类,必须重写接口中定义的所有方法。如果你觉得该方法不需要实现,你可以留空,但必须重写。

看我这句话:“接口只定义了方法的原型,即参数和方法名以及返回值,集成接口的类需要实现它。”

而且,接口(插座)实现以后,实现该接口的类(插头)必须符合接口的定义(插座和插口匹配)。其实,你会发现插座生产出来后,如果某电器的插头和插座不匹配,那么就无法使用该电器了。实际上,你在设计一个接口的时候,很难想到要怎么去设计,尽管你知道集成这个接口的类是怎么样的。就像如果你开一个工厂生产插线板,你在不知道电器,或不完全知道电器的插头如何设计的时候,你是很难生产出能用的插线板的。

那么,如何设计插线板呢?或者说如何设计接口呢?

先看看插线板厂商是如何生产的吧。

某天,有人生产一个电器是4个孔的,那就用不了了。这时候,插线板厂商为了生产出一种插线板,能适用于目前的大部分电器,也能适用于将来的电器,他找到了一个机构。机构是专门指定规则的,专门制定协议的。机构叫来了大部分的重要电器厂商的头头,和插线板老板一起开了个会。大家为了共同的利益,决定了一份协议。
协议是这样的:电器厂商以后生产的电器的插头,只能生产三孔的,但为了兼容目前市场上已有的电器,也能生产两孔的,但是尽量生产三孔的。而且孔的大小和之间的距离有明确的规定。插线板厂商的插线板也只能有两孔的和三孔的,而且孔的大小和之间的距离也必须按照协议来生产。
于是问题解决了,而且插线板厂商老板很聪明,他发现可以生产出既可以插两孔,又可以插三孔的插口,于是他的插线板大卖,他发财了。优秀的接口设计,给他带来了大大的好处,但他很聪明,他没忘记如果没有规范协议的机构,一切都是空白。

再补充几句吧,不然你还是难以理解。

当你想设计一个接口的时候,你最好先写几个将要继承这个几口的类,写几个只有框架而无实际内容的类,看看他们之间的共性,找到写接口的点,这就正如找电器老板来开会。写接口的时候,你需要在之前对接口进行说明,说明接口的适用范围,以及继承该接口的注意事项,这就好比请机构来制定协议规范。有了这些以后,你的接口在被使用的时候就不会错用,在写继承该接口的类的时候,也会按照规范完全的匹配接口。

最后一句话,即使你理解了我今晚所讲的每一句话,你还是不会写接口,因为你需要实践,实践才会出真知。最后这句话才是至理名言,我说的基本都是空话(在你学会了写接口后)。


第一次写接口时,第一个感觉就是,写接口跟没写一样。定义一个接口,马上去写实现类!其实此时就是用着面向过程的思路写程序,然后挂了个羊头,说起来怎么也有个接口了!

今天看了一位老兄写的对于接口的心得体会,真是太有同感了!

不要为了接口而接口,当你把自己不当做是个程序员来思考时,就能把用人的思想来思考了,你不会写程序,就不会考虑细节的实现了!此时你所关注的问题就是比较抽象的了,你看这不正符合面向对象的原则吗?当年张三丰教张无忌打太极就是要把招式全忘了,你要定义接口前就先忘了自己是个程序员吧!

当然不可能有100%的抽象,最终你还是要回到实现细节上来的,可此时你已是学会了太极的张无忌了!

python中的接口:

单纯为了代码重用的继承,在实际的使用中很容易造成“高耦合”的问题。
而接口继承,根据接口的概念“给使用接口的对象一个很好的抽象”,将对象的一些特征进行归一化处理,使外部调用者无需关心细节,可以一视同仁地处理特定接口的所有对象。

但是在python中没有interface的概念。在python中定义接口,仅仅是看起来像接口:

第一次写接口时,第一个感觉就是,写接口跟没写一样。定义一个接口,马上去写实现类!其实此时就是用着面向过程的思路写程序,然后挂了个羊头,说起来怎么也有个接口了!

接口提取了一群类共同的函数,可以把接口当做一个函数的集合。

然后让子类去实现接口中的函数。

这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。

归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。

抽象类

  • 定义:
    抽象类是一个特殊的类,他的特殊之处在于:只能被继承,不能被实例化

  • 为什么要有抽象类
    类是对相似的对象进行一个总结
    抽象类就是对有共同点的类的一种抽取总结(不完全说是总结,是因为类和类之间一定存在不同,不能完全统一总结成一类,真的是总结关系的话,应该就是子类和父类的关系了)。

从实现的角度来看,抽象类与普通类的不同在于:
抽象类只能有抽象方法(没有实现功能),这类不能被实例化,只能被继承,而且子类必须实现方法
从以上描述来看,抽象类非常类似接口,但是仍然是有差别的。

#_*_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)

由上面的代码可以看出,抽象类基本上实现了接口的功能,但是抽象类的本质还是一个类。指的是一组泪的相似性,包括数据属性和函数属性,而接口只强调函数属性的相似性。
所以,抽象类是一个介于类和接口之ijede一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计。

<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

转载于:https://www.cnblogs.com/scott-lv/p/7468949.html

12.面向对象(继承/super/接口/抽象类)相关推荐

  1. 笔记整理3----Java语言高级(三)11 综合练习+12 面向对象-static变量 与 代码块+13 面向对象-继承与抽象类+14 面向对象-接口与多态+15 面向对象-包修饰符

    11 综合练习+12 面向对象-static变量 与 代码块+13 面向对象-继承与抽象类+14 面向对象-接口与多态+15 面向对象-包&修饰符 第11天 综合练习 今日内容介绍 综合练习 ...

  2. python中继承和组合的区别_Py修行路 python基础 (十五)面向对象编程 继承 组合 接口和抽象类...

    一.前提回忆: 1.类是用来描述某一类的事物,类的对象就是这一类事物中的一个个体.是事物就要有属性,属性分为 1:数据属性:就是变量 2:函数属性:就是函数,在面向对象里通常称为方法 注意:类和对象均 ...

  3. Java模式设计卖电脑实验报告,面向对象(Java)实验0继承、接口和多态

    <面向对象(Java)实验0继承.接口和多态>由会员分享,可在线阅读,更多相关<面向对象(Java)实验0继承.接口和多态(11页珍藏版)>请在金锄头文库上搜索. 1.电子信息 ...

  4. 面向对象编程(十五)——抽象类和接口

    一.抽象(abstract)类 抽象类,说白了就是包含抽象方法的类.那什么是抽象方法?抽象方法是一种特殊的方法:抽象方法只有声明,而没有具体的实现.抽象方法说白了就是只有方法的声明,没有方法体. 抽象 ...

  5. 初始Java Java SE 包,继承,组合,多态,抽象类,接口

    目录 包 1.静态导入 2.将类放到包中 3.包的访问权限控制 继承 基本语法: 注意事项: protected 关键字 final 关键字 组合 多态 向上转型: 动态绑定 方法重写 重写的规则 : ...

  6. java 接口 抽象类 继承 重载 重写 多态

    4.1 抽象类 在面向对象领域由于抽象的概念在问题领域没有对应的具体概念,所以用以表征抽象概念的抽象类是不能实例化的. 同时,抽象类体现了数据抽象的思想,是实现多态的一种机制.它定义了一组抽象的方法, ...

  7. Java面向对象那些事之抽象类、接口

    目录 前言 一.抽象类 1.抽象类概述 2. 抽象类特点 3.抽象类成员特点 4.抽象类案例 二.接口 1.接口概述 2.接口特点 3.接口成员特点 4.接口案例 5.类和接口的关系 6.抽象类和接口 ...

  8. JAVA 继承基本类、抽象类、接口

    Java是一个面向对象的语言,java面向对象一般有三大特征:封装.继承.多态. 封装:就是把一些属性和方法封装到一个类里. 继承:就如子类继承父类的一些属性和方法. 多态:就如一个父类有多个不同特色 ...

  9. java 中普通类继承,抽象类继承,接口类继承,子类一定要重写父类中的方法吗

    一.简单总结,需要重写的有 普通类继承抽象类,重写所有抽象类方法:(不包括普通方法) 普通类继承接口,重写所有接口方法.(不包括default关键字修饰的方法) 详解见下: 普通类继承,并非一定要重写 ...

最新文章

  1. .net平台下C#socket通信(上)
  2. Spring Boot @ControllerAdvice 处理全局异常,返回固定格式Json
  3. Ambari2.7.4+HDP3.1.4.0中配置fair-scheduler
  4. 计算机控制试卷中南大学,期末试卷,需要的自取
  5. aix卸载java,AIX系统学习之--卸载软件错误
  6. BugkuCTF-Reverse题SafeBox(NJCTF)
  7. Atitit 文件存储标准化api 总结 目录 1. 操作系统,进行操作 1 1.1. FileUtils类的应用 1 1.2. 各大api 比较 2 2. Java。Io用apache的commo
  8. 爱的十个秘密--7.舍弃的力量
  9. WPS Linux版的公式自动编号且右对齐的方法
  10. android手机分区调整大小写,如何使用PQMagic调整磁盘分区容量大小
  11. 关于bss段的一些思考
  12. “熊猫烧香”制造者的起伏人生
  13. codewars练习(javascript)-2021/2/17
  14. 判定两颗二叉树是否相同
  15. 庖丁解牛linux内核,庖丁解牛Linux网络核心
  16. View 5应用之五:iPad与Android携带虚拟桌面
  17. STM32开发---F103系统时钟配置
  18. 我写了一个套路,助你随心所欲运用二分搜索
  19. Ext js 下拉框模糊匹配查询,并支持反复输入检索
  20. 重磅!图灵奖,公布!

热门文章

  1. 华为面试题: 杨辉三角形的变形
  2. 户口本识别/户口本OCR识别
  3. 地铁在终点站是怎么掉头的?南京地铁怎么过的长江?
  4. Mac环境下Tron 部署教程
  5. 黑喵桌面音乐播放器汉化版
  6. [附源码]JAVA+ssm交通违章举报平台(程序+Lw)
  7. [计算机一级MS备考]
  8. linux基础-快速入门
  9. 最新GraphPad Prism Mac直装版(医学绘图软件)v9.4.1
  10. 校友会小程序开发笔记十八:为浏览记录(我的足迹)模块的设计与实现