接口类
继承有两种用途:
一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
# 一:这样不好,我要统一一下支付的规则. class QQpay: def pay(self,money): print('使用qq支付%s元' % money) class Alipay: def pay(self,money): print('使用阿里支付%s元' % money) a = Alipay() a.pay(100) b = QQpay() b.pay(200) # 二,统一支付的规则 归一化设计,统一 pay接口 class QQpay: def pay(self,money): print('使用qq支付%s元' % money) class Alipay: def pay(self,money): print('使用阿里支付%s元' % money) def pay(obj,money): obj.pay(money) a = Alipay() b = QQpay() pay(a,100) pay(b,200) # 三,但是,来了一个野生程序员,他不知道你的约定俗成的规则,就会出问题 class QQpay: def pay(self,money): print('使用qq支付%s元' % money) class Alipay: def pay(self,money): print('使用阿里支付%s元' % money) class Wechatpay: def fuqian(self,money): print('使用微信支付%s元' % money) def pay(obj,money): obj.pay(money) a = Alipay() b = QQpay() pay(a,100) pay(b,200) c = Wechatpay() c.fuqian(300) # 四,解决方式 # 定义一个父类,什么都不写,只是要求继承我的所有类有一个pay方法,这样就制定了一个规范,这就叫做接口类,后者抽象类. class Payment: def pay(self):pass class QQpay(Payment): def pay(self,money): print('使用qq支付%s元' % money) class Alipay(Payment): def pay(self,money): print('使用阿里支付%s元' % money) class Wechatpay(Payment): def fuqian(self,money): print('使用微信支付%s元' % money) def pay(obj,money): obj.pay(money) a = Alipay() b = QQpay() pay(a,100) pay(b,200) c = Wechatpay() c.fuqian(300) #五,他还是不知道看你这些都继承了一个类,所以你要制定一个规范,强制他执行. # 创建一个规范 from abc import ABCMeta,abstractmethod class Payment(metaclass=ABCMeta): # 抽象类 接口类 规范和约束 metaclass指定的是一个元类 @abstractmethod def pay(self):pass # 抽象方法 class Alipay(Payment): def pay(self,money): print('使用支付宝支付了%s元'%money) class QQpay(Payment): def pay(self,money): print('使用qq支付了%s元'%money) class Wechatpay(Payment): # def pay(self,money): # print('使用微信支付了%s元'%money) def recharge(self):pass def pay(a,money): a.pay(money) a = Alipay() a.pay(100) pay(a,100) # 归一化设计:不管是哪一个类的对象,都调用同一个函数去完成相似的功能 q = QQpay() q.pay(100) pay(q,100) w = Wechatpay() pay(w,100) # 到用的时候才会报错 # 抽象类和接口类做的事情 :建立规范 # 制定一个类的metaclass是ABCMeta, # 那么这个类就变成了一个抽象类(接口类) # 这个类的主要功能就是建立一个规范 接口类示例
实践中,继承的第一种含义意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。
继承的第二种含义非常重要。它又叫“接口继承”。
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。
归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合——就好象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也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
为什么要有抽象类
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。
从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从实现角度来看,抽象类与普通类的不同之处在于:抽象类中有抽象方法,该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案
抽象类与接口类
抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。
抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念。
1.多继承问题
在继承抽象类的过程中,我们应该尽量避免多继承;
而在继承接口的时候,我们反而鼓励你来多继承接口
接口隔离原则:
使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口。
2.方法的实现
在抽象类中,我们可以对一些抽象方法做出基础实现;
而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现
多态
Pyhon不支持Java和C#这一类强类型语言中多态的写法,但是原生多态,其Python崇尚“鸭子类型”。
定义变量的方法:
1,java c# 需要定义 类型. int i= 3
2,java c# 没有多继承的概念.
鸭子类型: 它看着像鸭子,那么他就是鸭子.
python中好多不同类但同名的方法不是强制规定,而是约定俗成,像上面这三种类,都同样据有index方法,而且功能相似,
则 他们三个互称为鸭子.
class F1: pass class S1(F1): def show(self): print 'S1.show' class S2(F1): def show(self): print 'S2.show' # 由于在Java或C#中定义函数参数时,必须指定参数的类型 # 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类 # 而实际传入的参数是:S1对象和S2对象 def Func(F1 obj): """Func函数需要接收一个F1类型或者F1子类的类型""" print obj.show() s1_obj = S1() Func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show s2_obj = S2() Func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show Python伪代码实现Java或C#的多态 Python伪代码实现java C#多态
class F1: pass class S1(F1): def show(self): print 'S1.show' class S2(F1): def show(self): print 'S2.show' def Func(obj): print obj.show() s1_obj = S1() Func(s1_obj) s2_obj = S2() Func(s2_obj) Python “鸭子类型” 鸭子类型
封装
封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。
所以,在使用面向对象的封装特性时,需要:
- 将内容封装到某处
- 从某处调用被封装的内容
1,封装 对象的封装.
class Person: def __init__(self,name,age): self.name = name self.age = age p1 = Person('oldboy',1000) p2 = Person('alex',10000) print(p1.name) print(p2.name)
2 封装(私有成员.)
类的结构分析:
class Person: mind = '有思想...' # 第一部分:所有的公有静态变量,公有静态字段 __level = '高等动物' # 第一部分:私有静态变量,私有静态字段 def __init__(self,name,age,sex): # 构造方法 # 第二部分 动态方法,方法(函数) self.name = name # 公有对象属性 self.age = age self.__sex = sex # 私有对象属性 def func(self): # 第二部分:普通方法 print(666) def __func1(self): # 第二部分:私有方法 print(777) @staticmethod # 静态方法 def f2():pass @classmethod # 类方法 def f2(self): pass @property # 属性 def hex(self):pass
类整体分类:
第一部分: 公有静态字段 mind = '有思想...' ,私有静态字段. __level = '高等动物'
第二部分: 特殊方法(__init__(公有属性,私有属性),__str__...)
普通方法 def func(self)
私有方法 def __func1(self):
类方法:
@classmethod
def f2(self): pass
静态方法:
@staticmethod # 静态方法
def f2():pass
属性:
@property # 属性
def hex(self): pass
私有成员: 私有静态字段,私有属性,私有方法 在变量前+ __双下划线.
私有静态字段
class Animal: __cloth = '皮毛' # _Animal__cloth class Person(Animal): mind = '有思想...' # 第一部分:所有的公有静态变量,公有静态字段 __level = '高等动物' # 第一部分:私有静态变量,私有静态字段 # _Person__level def __init__(self,name,age): # 构造方法 # 第二部分 动态方法,方法(函数) self.name = name # 公有对象属性 self.age = age def func(self): print(self.__level) print(self._Animal__cloth) print(self.__cloth)
在类的外面访问: 私有静态字段是访问不到的 p1 = Person('alex',1000) print(p1.mind) print(p1.__level) print(Person.__level) print(Person.__dict__) print(Person._Person__level) 可以通过对象._类名__变量名 类名._类名__变量名 可以访问到,但是绝对不要这么访问. 在类的内部: 私有静态字段是可以访问 p1 = Person('alex',1000) p1.func() 父类的私有静态字段,派生类可否访问? 不可访问. p1 = Person('alex',10) print(p1.__cloth) p1.func() 私有方法 class Animal: def __f1(self):print(1111) # _Animal__f1 class Person(Animal): mind = '有思想...' # 第一部分:所有的公有静态变量,公有静态字段 def __init__(self,name,age): # 构造方法 # 第二部分 动态方法,方法(函数) self.name = name # 公有对象属性 self.age = age self.__sex = sex def __func(self): # _Person__func() print(666) def func1(self): self.__func() # self._Person__func() def func2(self): self.__f1() # self._Person__f1() 类外面访问不到. p1 = Person('OLDBOY',1000) p1.__func() # 类内部可以方法. p1 = Person('OLDBOY',1000) p1.func1() 派生类中也是不能访问的. p1.func2() 私有属性 也是类外部不能访问,派生类不能访问,只能在类内部访问. class Animal: def __f1(self):print(1111) # _Animal__f1 class Person(Animal): mind = '有思想...' # 第一部分:所有的公有静态变量,公有静态字段 def __init__(self,name,age,sex): # 构造方法 # 第二部分 动态方法,方法(函数) self.name = name # 公有对象属性 self.age = age self.__sex = sex def __func(self): # _Person__func() print(666)
总结: 对于私有成员来说,他加载到内存时,都会加上_类名__变量名,所以你在类的外部,或者派生类中都不可访问.
为什么设置私有成员?
有些变量,方法,属性,只在类内部进行使用即可,不便于(不允许)类外部或者派生类去调用.