Python_015(面向对象(接口类,抽象类,多态,封装)
一.抽象类与接口类
1.抽象类:抽象即类似或者说比较像的部分,继承描述的是父类与子类的一种关系,要找出这种关系,必须先抽象再继承;
a:抽象分成两个层次:
1)由对象->类:将两个有相似地方的对象抽取成类;
2)由类->父类:将两个有相似地方的类抽取成父类;
:抽象最主要的作用就是划分类别(可以隔离关注点,降低复杂度)
2.为什么要有抽象类
与Java一样,Python也有抽象类的概念,需要借助模块实现,它是一个特殊的类,特殊之处在于只能被继承,不能被实例化;
类是从一些对象中抽取相同的内容而来的,那如果类中有相似的地方,就引出了抽象类概念,
所以从实现的角度来看,抽象类与普通类的不同之处在于:抽象类只能有抽象方法(没有实现功能),这个类不能被实例化,只能被继承,且子类必须实现抽象方法;
#用代码来表示一下接口类和抽象类:
1 #版本一 2 class Alipay: 3 def __init__(self,money): 4 self.money = money 5 def pay(self): 6 print('阿里支付了%s元!' % self.money) 7 class Jdpay: 8 def __init__(self,money): 9 self.money = money 10 def pay(self): 11 print('京东支付了%s元!' % self.money) 12 p1 = Alipay(1000) 13 p1.pay() #这里是Alipay.pay() 14 p2 = Jdpay(1000) 15 p2.pay() #这里是Jdpay.pay() 16 #输出结果: 17 阿里支付了1000元! 18 京东支付了1000元!
#虽然你调用pay方法格式没有错,但是不合理,工作中如果有人接着你的程序写一个微信支付,它写的格式和你的可能就不一样,不能体现归一化的设计理念;
2)所以引出第二版(接口的概念)
a:接口提取了一群类共同的函数,可以把接口当做一个函数的集合,然后让子类去实现接口中的函数
这么做的意义在于归一化,归一化就是只要基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,
从用法上来说都一样;
归一化的好处在于:
1. 归一化让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
2. 归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合
代码展示:
1 #版本二 2 class Alipay: 3 def __init__(self,money): 4 self.money = money 5 def pay(self): 6 print('阿里支付了%s元!' % self.money) 7 class Jdpay: 8 def __init__(self,money): 9 self.money = money 10 def pay(self): 11 print('京东支付了%s元!' % self.money) 12 def pay(obj): #接口函数(约定俗成,内行程序员大家都看的懂) 13 obj.pay() 14 p1 = Alipay(1000) 15 pay(p1) 16 p2 = Jdpay(1000) 17 pay(p2) 18 #输出结果: 19 阿里支付了1000元! 20 京东支付了1000元!
**这种做法相对于有素质的程序员行,但是万一来个野生程序员,还按版本一的方式写,就又乱套了;
所以引出第三种写法:抽象类(类似于接口)起规范化作用:
1 class Pay(metaclass=ABCMeta): 2 all_type = 'alex' 3 @abstractmethod #每一个子类中都要有这个方法,定义格式 4 def pay(self):pass #规范一个方法上面加一句@abstractmethod 5 class Alipay(Pay): 6 def __init__(self,money): 7 self.money = money 8 def pay(self): 9 print('阿里支付了%s元!' % self.money) 10 class Jdpay(Pay): 11 def __init__(self,money): 12 self.money = money 13 def pay(self): 14 print('京东支付了%s元!' % self.money) 15 class Wxpay(Pay): 16 def __init__(self,money): 17 self.money = money 18 def wupay(self): #这里没有pay方法,再实例化对象时会报错 19 pass 20 p1 = Wxpay(200) 21 #输出结果: 22 TypeError: Can't instantiate abstract class Wxpay with abstract methods pay
二.多态
1.多态概念:多态就是一个对象有多种状态,例如文件有可执行文件和文本文件;
2.多态性:这是指在不考虑实例类型的情况下使用实例
就比如:老师,下课铃响了(),学生,下课铃响了(),老师执行的是下班操作,学生执行的是方向操作,
虽然二者消息一样,但是执行的效果不同;
3.多态的好处:a:增加了程序的灵活性 b:增加了程序的可扩展性
4.鸭子类型:Python崇尚鸭子类型,即"如果看起来像,叫声而且走路像鸭子,那么它就是鸭子",其实Python中没有多态的概念,因为它处处都是多态,不用考虑数据类型,传入函数,封装到对象都可以.
举例1:你只要像鸭子,我就可以用鸭子的方法或属性对你使用;
利用标准库中定义的各种‘与文件类似’的对象,尽管这些对象的工作方式像文件,但他们没有继承内置文件对象的方法
#二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用 class TxtFile: def read(self): pass def write(self): pass class DiskFile: def read(self): pass def write(self): pass
例2:其实大家一直在享受着多态性带来的好处,比如Python的序列类型有多种形态:字符串,列表,元组,多态性体现如下:
#str,list,tuple都是序列类型 s=str('hello') l=list([1,2,3]) t=tuple((4,5,6)) #我们可以在不考虑三者类型的前提下使用s,l,t s.__len__() l.__len__() t.__len__() len(s) len(l) len(t)
三.封装
1. a:广义的封装:
实例化一个对象,给对象空间封装一些属性;
b:狭义的封装:私有制,私有静态字段,私有方法,私有对象属性;
格式为__变量名或则__方法名
2.私有封装的特点
a:实例化的对象不能在外访问私有静态字段;
b:类名在外不能访问静态字段; 所以对于私有静态字段,类的外部和派生类都不能访问;
c:对于私有静态字段来说,只有在本类内部方法或属性中可以访问
3.讲解在定义私有封装属性是Python内部机制
在Python中用双下划线开头的方式将属性隐藏了起来(设置成私有的)
#其实这仅仅这是一种变形操作且仅仅只在类定义阶段发生变形 #类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式: class A: __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N def __init__(self): self.__X=10 #变形为self._A__X def __foo(self): #变形为_A__foo print('from A') def bar(self): self.__foo() #只有在类内部才可以通过__foo的形式访问到. #A._A__N是可以访问到的, #这种,在外部是无法通过__x这个名字访问到。
所以在函数外部,你若想要访问函数私有变量或方法,只需要在前面加上_类名__方法名()即可;
4.封装的意义
1).封装数据:将数据隐藏起来这不是目的,隐藏起来后对外提供接口时,我们可以直接在接口上对该数据操作做出限制,以此完成对数据属性操作的严格控制;
class Teacher: def __init__(self,name,age): # self.__name=name # self.__age=age self.set_info(name,age) def tell_info(self): print('姓名:%s,年龄:%s' %(self.__name,self.__age)) def set_info(self,name,age): if not isinstance(name,str): raise TypeError('姓名必须是字符串类型') if not isinstance(age,int): raise TypeError('年龄必须是整型') self.__name=name self.__age=age t=Teacher('egon',18) t.tell_info() t.set_info('egon',19) t.tell_info()
2).封装方法:不能直接调用我的方法,你只能看到一个最终的接口
封装方法举例:
1. 你的身体没有一处不体现着封装的概念:你的身体把膀胱尿道等等这些尿的功能隐藏了起来,然后为你提供一个尿的接口就可以了(接口就是你的。。。,),你总不能把膀胱挂在身体外面,上厕所的时候就跟别人炫耀:hi,man,你瞅我的膀胱,看看我是怎么尿的。
2. 电视机本身是一个黑盒子,隐藏了所有细节,但是一定会对外提供了一堆按钮,这些按钮也正是接口的概念,所以说,封装并不是单纯意义的隐藏!!!
3. 快门就是傻瓜相机为傻瓜们提供的方法,该方法将内部复杂的照相功能都隐藏起来了
提示:在编程语言里,对外提供的接口(接口可理解为了一个入口),可以是函数,称为接口函数,这与接口的概念还不一样,接口代表一组接口函数的集合体。
#取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱 #对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做 #隔离了复杂度,同时也提升了安全性 class ATM: def __card(self): print('插卡') def __auth(self): print('用户认证') def __input(self): print('输入取款金额') def __print_bill(self): print('打印账单') def __take_money(self): print('取款') def withdraw(self): self.__card() self.__auth() self.__input() self.__print_bill() self.__take_money() a=ATM() a.withdraw()