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 NotImplementedErrorclass Wechatpay(Payment):def fuqian(self,money):print('微信支付了%s元'%money)p = Wechatpay() #这里不报错 pay(p,200) #这里报错了
借用abc模块来实现接口(抽象类)
from abc import ABCMeta,abstractmethodclass Payment(metaclass=ABCMeta):@abstractmethoddef pay(self,money):passclass 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)
转载于:https://www.cnblogs.com/Xanderzyl/p/10665884.html
python 接口(抽象) 多态,鸭子类型, 多继承原理(mro)相关推荐
- python 全栈开发,Day21(抽象类,接口类,多态,鸭子类型)
一.昨日复习 派生方法和派生属性 super 只有在子父类拥有同名方法的时候, 想使用子类的对象调用父类的方法时,才使用super super在类内 : super().方法名(arg1,..) 指名 ...
- Python 语言中的 “鸭子类型”
Python 语言中的 "鸭子类型" 继承 多态 鸭子类型 不要检查它是不是鸭子.它的叫声像不像鸭子.它的走路姿势像不像鸭子,等等.具体检查什么取决于你想使用语言的哪些行为.(co ...
- 25 类:接口 抽象父类 多态 鸭子类型 格式化方法与析构方法 反射 异常处理 自定义异常 断言...
面向对象与面向过程分析 # 清晰知道操作的功能,但不明确操作的具体对象 print(len('123')) # 清晰知道操作的对象,但不明确具体的操作方法 print('123'.__len__()) ...
- 面向对象的接口类 以及鸭子类型
1.接口类,抽象类. 2.鸭子类型(Python多态)(Python三大特性之一)Python封装 1.接口类和抽象类只是在工作中书写的一种规范. class QQ: def pay(self,mo ...
- 1023day5:class类属性方法、每次执行类属性+1、内建模块、时间装饰器wrapper、面向对象__slots__方法:限制类的属性等基础知识、正则表达式基础知识、多态鸭子类型
文章目录 一.类class 1.Python类class 属性 方法 2.类的构造方法__init__() 3.每次执行一次类的属性+1 二.模块 1.内建模块 2.第三方模块 3.定义自己的模块 三 ...
- 【Python】浅谈 鸭子类型 (Duck Typing)
目录 一.来源 二.说明 三.举例 四.不足 一.来源 在程序设计中,鸭子类型 (duck typing) 是动态类型的一种风格.在此风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口, ...
- Python3中的多态 ,鸭子类型
多态的概念 : -一个对象具有多种形态 , 在不同的使用环境中以不同的形态展示其功能 , 称该对象具有多态特征 鸭子类型 : -鸭子类型是一种特殊的调用现象 , 当对象在语法层面能够满足调用关系 , ...
- Python鸭子类型和多态
补充:list.extend()里面不只是list只要是可迭代对象都可,包括生成器等 getitem魔法函数一定要有报错条件退出 def __getitem__(self, item):return ...
- python与鸭子类型
目录 1.面向对象(OOP)的三大特征 2.静态类型语言和动态类型语言的区别 3.面向接口编程 4.鸭子类型 5.python中的多态 1.面向对象(OOP)的三大特征 (1)面向对象程序设计有三大特 ...
最新文章
- 关于mac的一些常用操作记录
- python好学不-Python爬虫好学吗?
- java变量访问权限_JAVA成员变量的访问权限
- linux与linux传文件乱码,关于Linux与windows传递文件乱码问题
- 小技巧 ----- Java算法题标准模版
- ftp 相关知识集合
- 关系型数据库一致性的理解
- 【转】C#字符串转换为日期
- 知其然,知其所以然——ArrayList.add()详解
- 数据库cosc2406-2407辅导week10-quiz-Transactions and Concurrency
- 【微信小程序】一文读懂页面导航
- tv电视焦点(遥控器)操作笔记jq
- 时态二--(专升本语法)
- 15.4 真实感图形——纹理映射
- 点击跳出商务通对话框
- 有五个人坐一起,问第五个多少岁?(java)
- c语言万年历报错系统,用c语言写万年历应该怎样写?
- BUG InvalidArgumentError (see above for traceback): You must feed a value for placeholder tensor 'Pl
- 语录集人生---建议
- ckeditor4.6.2上传图片thinphp3.3.2
热门文章
- z370支持pcie信号拆分吗_不再混乱 PCI-E插槽都有哪些样子?
- 【方案开发】医用级人体体温计额温仪方案
- 浅析10kV~35kV 变电站综合自动化系统设计与应用
- IPNC_RDK_McFW_UserGuide
- 视觉SLAM-回环检测
- 南京月薪一万怎么样?
- 八十年代的计算机游戏,梦回黄金时代 80年代最伟大的20款游戏_单机游戏_新浪游戏_新浪网...
- Python + Neo4j(安装)可视化分析漫威十年人物关系图谱
- 回顾:Unity 游戏引擎发展历程
- VC6.0 Html Edit 编辑框