python 接口(抽象) 多态,鸭子类型, 多继承原理(mro)
抽象类与接口类
接口类
继承有两种用途:
一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
开发中容易出现的问题

- class Alipay:
- '''
- 支付宝支付
- '''
- def pay(self,money):
- print('支付宝支付了%s元'%money)
- class Applepay:
- '''
- apple pay支付
- '''
- def pay(self,money):
- print('apple pay支付了%s元'%money)
- class Wechatpay:
- def fuqian(self,money):
- '''
- 实现了pay的功能,但是名字不一样
- '''
- print('微信支付了%s元'%money)
- def pay(payment,money):
- '''
- 支付函数,总体负责支付
- 对应支付的对象和要支付的金额
- '''
- payment.pay(money)
- p = Wechatpay()
- pay(p,200) #执行会报错

接口初成:手动报异常:NotImplementedError来解决开发中遇到的问题

- class Payment:
- def pay(self):
- raise NotImplementedError
- class Wechatpay(Payment):
- def fuqian(self,money):
- print('微信支付了%s元'%money)
- p = Wechatpay() #这里不报错
- pay(p,200) #这里报错了

借用abc模块来实现接口(抽象类)

- from abc import ABCMeta,abstractmethod
- class Payment(metaclass=ABCMeta):
- @abstractmethod
- def pay(self,money):
- pass
- class Wechatpay(Payment):
- def fuqian(self,money):
- print('微信支付了%s元'%money)
- p = Wechatpay() #不调就报错了

实践中,继承的第一种含义意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。
继承的第二种含义非常重要。它又叫“接口继承”。
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。
归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合——就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。
- 依赖倒置原则:
- 高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该应该依赖细节;细节应该依赖抽象。换言之,要针对接口编程,而不是针对实现编程
在python中根本就没有一个叫做interface的关键字,上面的代码只是看起来像接口,其实并没有起到接口的作用,子类完全可以不用去实现接口 ,如果非要去模仿接口的概念,可以借助第三方模块:
http://pypi.python.org/pypi/zope.interface
twisted的twisted\internet\interface.py里使用zope.interface
文档https://zopeinterface.readthedocs.io/en/latest/
设计模式:https://github.com/faif/python-patterns

- 接口提取了一群类共同的函数,可以把接口当做一个函数的集合。
- 然后让子类去实现接口中的函数。
- 这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。
- 归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
- 比如:我们定义一个动物接口,接口里定义了有跑、吃、呼吸等接口函数,这样老鼠的类去实现了该接口,松鼠的类也去实现了该接口,由二者分别产生一只老鼠和一只松鼠送到你面前,即便是你分别不到底哪只是什么鼠你肯定知道他俩都会跑,都会吃,都能呼吸。
- 再比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样
- 为何要用接口

抽象类
什么是抽象类
与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
为什么要有抽象类
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。
从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从实现角度来看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案
在python中实现抽象类

- '''抽象类'''
# import abc
# class Animal(metaclass=abc.ABCMeta): # 抽象类,只能被继承,不能被实例化
# @abc.abstractmethod # 子类必须定义
# def run(self):
# pass
# @abc.abstractmethod # 子类必须定义
# def eat(self):
# pass
# class People(Animal):
# def run(self):
# print('is walking')
# def eat(self):
# print('is eating')
# class Pig(Animal):
# def run(self):
# print('is running')
# def eat(self):
# print('is eating')
# class Dog(Animal):
# def run(self):
# print('is zou')
# def eat(self):
# print('is eating')
# peo1 = People()
# pig1 = Pig()
# dog1 = Dog()
#一切皆文件
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)

鸭子类型
- '''鸭子类型(看起来像鸭子,就当成鸭子来做。不一定要继承父类,‘是’鸭子)'''
- # class File:
- # def read(self):
- # pass
- # def write(self):
- # pass
- # class Disk:
- # def read(self):
- # print('disk read')
- # def write(self):
- # print('disk write')
- # class Text:
- # def read(self):
- # print('text read')
- # def write(self):
- # print('tsxt write')
- # disk = Disk()
- # text = Text()
- #
- # disk.read() # 看起来像文件,就把它当成文件来操作
- # disk.write()
- # text.read()
- # text.write()
- # # 序列类型:列表list,元祖tuple,字符串str
- # s=str('hello')
- # l=list([1,2,3])
- # t=tuple((4,5,6))
- #
- # #我们可以在不考虑三者类型的前提下使用s,l,t
- # print(s.__len__())
- # print(l.__len__())
- # print(t.__len__())
- #
- # print(len(s))
- # print(len(l))
- # print(len(t))
抽象类与接口类
抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。
抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念。
1.多继承问题
在继承抽象类的过程中,我们应该尽量避免多继承;
而在继承接口的时候,我们反而鼓励你来多继承接口
- 接口隔离原则:
- 使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口。
2.方法的实现
在抽象类中,我们可以对一些抽象方法做出基础实现;
而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现
钻石继承
继承顺序

- 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中才分新式类与经典类
- 继承顺序

继承原理
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如
- '''2.super() (依赖于继承)(按照mro列表查找)'''
# class Hero:
# def __init__(self, nickname, life_value, aggresivity):
# self.nickname = nickname
# self.life_value = life_value
# self.aggresivity = aggresivity
# def attack(self, one):
# one.life_value -= self.aggresivity
# class Jie(Hero):
# camp = 'huoyingjie'
# def __init__(self, nickname, life_value, aggresivity, wapen):
# super(Jie, self).__init__(nickname, life_value, aggresivity) # 依赖继承,按mro列表查找
# # super().__init__(nickname, life_value, aggresivity) # 简写
# self.wapen = wapen
# def attack(self, one):
# super(Jie, self).attack(one) # 依赖继承
# print('from Jie class')
# class Mangsheng(Hero):
# camp = 'xiazi'
# j1 = Jie('qwe', 100, 20, '双刀')
# m1 = Mangsheng('asd', 120, 50)
# j1.attack(m1)
# print(m1.__dict__)
# print(j1.__dict__)
'''A没有继承B,但是A内super会基于C.mro()继续往后找'''
# class A:
# def test(self):
# print('from A')
# super().test()
# class B:
# def test(self):
# print('from B')
# class C(A,B):
# pass
#
# c=C()
# c.test() #打印结果:from B
#
# print(C.mro())
# #[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
- >>> F.mro() #等同于F.__mro__
- [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
继承小结
继承的作用
- 减少代码的重用
- 提高代码可读性
- 规范编程模式
几个名词
- 抽象:抽象即抽取类似或者说比较像的部分。是一个从具题到抽象的过程。
- 继承:子类继承了父类的方法和属性
- 派生:子类在父类方法和属性的基础上产生了新的方法和属性
抽象类与接口类

- 1.多继承问题
- 在继承抽象类的过程中,我们应该尽量避免多继承;
- 而在继承接口的时候,我们反而鼓励你来多继承接口
- 2.方法的实现
- 在抽象类中,我们可以对一些抽象方法做出基础实现;
- 而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现

钻石继承
- 新式类:广度优先 py3
- 经典类:深度优先 py2
多态
- '''多态(同种事物的多种形态)'''
- # import abc
- # class Animal(metaclass=abc.ABCMeta): # 同一类事物:动物
- # @abc.abstractmethod
- # def talk(self):
- # pass
- # class People(Animal): # 动物形态之一:人
- # def talk(self):
- # print('people is talking')
- # class Dog(Animal): # 动物形态之二:狗
- # def talk(self):
- # print('dog is talking')
- # class Pig(Animal): # 动物形态之三:猪
- # def talk(self):
- # print('pig is talking')
- # peo1 = People()
- # dog1 = Dog()
- # pig1 = Pig()
- # '''多态性(可以不考虑对象的类型是情况下直接使用)'''
- # # peo1.talk() # 动态多态性,静态多态性:+
- # # dog1.talk()
- # # pig1.talk()
- # def func(animal):
- # animal.talk()
- # func(peo1)
- # func(dog1)
- # func(pig1)
python 接口(抽象) 多态,鸭子类型, 多继承原理(mro)的更多相关文章
- python 全栈开发,Day21(抽象类,接口类,多态,鸭子类型)
一.昨日复习 派生方法和派生属性 super 只有在子父类拥有同名方法的时候, 想使用子类的对象调用父类的方法时,才使用super super在类内 : super().方法名(arg1,..) 指名 ...
- python之类的多态(鸭子类型 )、封装和内置函数property
一.多态 1.什么是多态:一个类表现出的多种状态--->通过继承来实现的例如:class Animal:passclass Dog(Animal):passclass Cat(Animal):p ...
- 组合+封装+property+多态+鸭子类型(day21)
目录 昨日回顾 继承 什么是继承 继承的目的 什么是抽象 继承背景下,对象属性的查找顺序 派生 什么是派生 子类派生出新的属性,重用父类的属性 新式类和经典类 钻石继承的继承顺序 今日内容 一.组合 ...
- 多态 鸭子类型 反射 内置方法(__str__,__del__) 异常处理
''' 1什么是多态 多态指的是同一种/类事物的不同形态 2 为何要有多态 多态性:在多态的背景下,可以在不用考虑对象具体类型的前提下而直接使用对象 多态性的精髓:统一 多态性的好处: 1增加了程序的 ...
- day 28 面向对象 三种特性之一 多态 鸭子类型 反射(反省)
多态是OOP的三大特征之一 字面意思:多种形态 多种状态 官方描述:不同的对象可以响应(调用)同一个方法 产生不同的结果(例如水的三相特征) 多态不是什么新技术 我们编写面向对象的程序时 其实就有多态 ...
- 第7.3节 Python特色的面向对象设计:协议、多态及鸭子类型
Python是一种多态语言,其表现特征是:对象方法的调用方只管方法是否可调用,不管对象是什么类型,从而屏蔽不同类型对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化. 一. P ...
- PythonI/O进阶学习笔记_3.1面向对象编程_python的多态和鸭子类型
前言: 与第一篇的面向对象内容不同的是,第一篇中的面向对象更多的是与类.对象结合起来的概念粗浅理解,就是在编程历史中诞生的一种思想方法. 这篇的面向对象编程,更多落实到在语言设计实现中,是如何体现面向 ...
- python与鸭子类型
部分参考来源:作者:JasonDing https://www.jianshu.com/p/650485b78d11##s1 首先介绍下面向对象(OOP)的三大特征: (1)面向对象程序设计有三大特 ...
- python中对多态的理解
目录 python中对多态的理解 一.多态 二.多态性 三.鸭子类型 python中对多态的理解 一.多态 多态是指一类事物有多种形态,比如动物类,可以有猫,狗,猪等等.(一个抽象类有多个子类,因而多 ...
随机推荐
- 实践作业4---DAY4阶段三。
阶段三:给出结论 这一阶段,我们首先列表从核心功能.细节.用户体验.辅助功能差异化功能.软件的适应性和成长性展开.我们得结论前参考了权威网站数据.并自己也做了相应分析. 结论:经过这么多工作,这个软件 ...
- MYSQL隐式类型转换
MYSQL隐式类型转换 关于官方文档中的理解大致是: 如果两个参数比较,有至少一个NULL,结果就是NULL,除了是用NULL<=>NULL 会返回1.不做类型转换 两个参数都是字符串,按 ...
- QT学习之事件处理
Qt事件机制 Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.. Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. Qt事件的类型很多, 常见的qt的事件如下: 键盘事 ...
- (转) c/c++调用libcurl库发送http请求的两种基本用法
libcurl主要提供了两种发送http请求的方式,分别是Easy interface方式和multi interface方式,前者是采用阻塞的方式发送单条数据,后者采用组合的方式可以一次性发送多条数 ...
- 从零开始学习前端JAVASCRIPT — 8、JavaScript基础COOKIE
1:通信协议 定义:通信协议是指双方实体完成通信或服务所必须遵循的规则和约定.协议定义了数据单元使用的格式,信息单元应该包含的信息与含义,连接方式,信息发送和接收的时序,从而确保网络中数据顺利地传送到 ...
- Maven及POM文件
Maven Maven是基于项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具. Logback是由LOG4创始人设计的又一个开源日志组件. 相关链接: Ma ...
- linux 学习管道 和重定向
开源文化的理念之一 就是不要重新发明轮子 在linux 系统中大多是都是非常简单的命令,每个命令都是实现一个或者几个简单的功能,我们可以将不同的命令组合在一起 来达到复杂的功能的目的,在linux中因 ...
- Understanding sun.misc.Unsafe
转自: https://dzone.com/articles/understanding-sunmiscunsafe The biggest competitor to the Java virtua ...
- NIOS II 软件程序固化的相关知识
片上RAM和ROM的SOPC系统1.生成hex文件2.将hex文件添加到quartus工程中(添加qip文件)3.对工程进行全编译4.下载sof就可以看到程序运行5.将sof转换为jic文件,烧写到E ...
- Javascript基础编程の面向对象编程
javascript是解释型的语言,在编译时和运行时之间没有明显区别,因此需要更动态的方法.javascript没有正式的类的概念,我们可以使用在运行时创建新的对象类型来替代,并且可以随时更改已有对象 ...