1. 封装:
1.1 封装:
1.1.1 抽象:抽象是从众多的事物中抽取出共同的、本质性的特征,而舍弃其非本质的特征;
1.1.2 封装:将众多的有共性的事物的相同属性(静态及动态)抽象归纳出来,然后打包封装起来,封装其实就是定义”类“的过程,目的是增强程序安全性、简化编程,让修改或扩展也更方便;
1.1.3 接口:封装的内容对外只为使用者提供接口(一般是指类的自定义方法),即只能通过接口访问或调用封装的内容,如果无需接口可直接访问,那类的封装特性就没有了;
1.2 类属性的私有化
1.2.1 概念:把类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问;
1.2.2 创建方式:是在属性或方法名字前面加两个下划线即可;
1.2.3 私有化属性的继承:父类的私有属性与方法也可以跟普通的属性与方法一样,可以被继承和重写
案例详见:ex41b.py
2. 继承:目的是代码重用,其实重写一遍也是一样,只是麻烦而以
2.1 概念:
当某一类物B事拥有了另一类物事A的全部特征,我们就说B类继承了A类(写法:class B(A):),被继承类称为父类(基类),继承类称为子类(派生类),或者说:我们已经定义了A类的特征,当我们想让B类拥有A类的全部特征,我们不必为B重新定义A所具的特征,我们只让B继承A即可,如果:class阿克苏苹果(苹果),class苹果(水果),等等 [ 备注:阿克苏苹果是青苹果类下的一个品种!]
2.2 类的分类:
2.2.1 经典类:在Python 2.x 中默认都是经典类 ;
2.2.2 新式类: 【现在都是新类】
2.2.2.1 在Python 2.x 中,只有显示继承了最高级的object类(如class A(object) 才是新式类,即只有object类的子类、孙类、从孙类、等才是新式类;
2.2.2.2 Python 3.x 中,默认都是新式类 , 不管有没有显示的继承了最高级的object类,都认为是新式类;
2.3 类的继承方式:
2.3.1 单继承:只以一个类作为父类进行继承,可以多层继承,如:class B(A),class C(B),即B继承A,C又继承B; 【多用这个】
2.3.2 多继承:同时以多个类做为父类进行继承,但这几个父类又都有同名的变量或方法,则按以下的方式选择其中一个继承:【少用这个】
2.3.2.1 当类是经典类时,多继承情况下,会按照深度优先方式查找及继承;
2.3.2.2 当类是新式类时,多继承情况下,会按照广度优先方式查找及继承;【现在都是新式类,均此方式】
- 第一步:先在父类中从左到右查找继承,父类中同名的变量或方法只继承前者,不同名的变量或方法全部继承;
- 第二步:在爷类中,有变量或方法与父类同名,只从父类继承,不同名的变量或方法全部继承;
实例:见附件,因为不多用,不多说
2.4 多重继承下的super() 函数
2.4.1 概念: 函数是用于调用父类(超类)的一个方法, 用来解决多重继承问题,如果单继承就可以用调用父类方法的方式解决, 但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表;暂不管
2.4.1.1 多个父类会根据它们在列表中的顺序被检查;
2.4.1.2 如果对下一个类存在两个合法的选择,选择第一个父类;
2.4.1.3 一般的使用场景:与init()组合,帮我们将父类的属性复制到子类
2.4.2 语法:
2.4.2.1 在Python 3版本:super().at
- at:父类的属性或方法
2.4.2.2 Python2.x版本:super(Class, self).at - Class:当前子类名称
- at:父类的属性或方法名
案例详见:ex41e.py
class people: #定义一个people类
na = 'Hu' #定义类公有属性变量
name = 'Human' #定义类公有属性变量
weight = 100 #定义类公有属性变量
__MinIQ = 90 #定义类私有属性变量,在属性名字前面加两个下划线,属性就被定义为私有属性,只允许类里面的函数访问,外部无法直接进行访问
def __init__(self, n, a, w): # 定义类的实例的构造函数,第一个参数是self,表示对象,第二个及后面参数才真正是这个类的属性参数
self.name = n # 定义类的实例变量(普通属性),可以重写覆盖类变量,类外部的函数可以访问
self.__age = a # 定义类的实例变量(私有属性),在属性名字前面加两个下划线,属性就被定义为私有属性,只允许类里面的函数访问,外部无法直接进行访问
self.weight = w # 定义类的实例变量(普通属性),可以重写覆盖类变量,类外部的函数可以访问
def Nextyear1(d): # 定义一个类的方法(注意:不是实例方法,参数也没有self),可以通过类进行调用,不可以通过实例对象进行调用
Nextw = people.weight - d # 因为是类方法,所以它也只能访问类变量,不能访问实例变量
return Nextw # 返回计算后的结果给调用者
def __Nextyear2(self, d): # 定义一个私有属性的实例方法(第一个参数是self,表示对象,第二个参数才是这个实例方法的真正参数),
# 这个方法在实例过程中是会被传递给实例对象,所以可以通过对象来调用这个方法
# 在方法名字前面加两个下划线,方法就被定义为私有方法,只允许类里面的函数调用,外部无法直接进行调用
Nextw = self.weight - d # 因为是实例方法,所以它访问的也是实例对象的属性,注意:实例属性是可以重定覆盖类属性
return Nextw # 返回计算后的结果给调用者
def speak(self, lw): # 定义一个普通属性的实例方法(第一个参数是self,表示对象,第二个参数才是这个实例方法的真正参数)
# 这个方法在实例过程中是会被传递给实例对象,所以可以通过对象来调用这个方
print("{} said: Im {} year old,my weight is {}!".format(self.name,self.__age, self.weight)) # 这个方法可以对类的参数进行一些操作
print(f"I can speak {lw}!") # 这个方法还可以对自己的参数进行一些操作
print(f"Next year,my weight will be {people.Nextyear1(10)}!") # 可以调用类里面的类方法,执行结果是100-10 = 90
print(f"Next year,my weight will be {self.__Nextyear2(10)}!") # 可以调用实例对象的实例方法,
# 在实例化时,实例对象的weight被重写为144,执行结果是144-10 = 134
print("We're smart. We're all over {}!".format(self.__MinIQ )) # __MinIQ是类私有属性变量,只允许类里面的函数访问
print("We all belong to ",people.name) # name类公有属性变量,类外部的函数可以通过类进行访问
# print(f"We're smart. We're all over {people.__MinIQ}!") # 报错,因为__MinIQ被定义是私类私有属性变量,只能允许类里面的函数访问,类外的外部函数无法访问
print(f"Next year,my weight will be {people.Nextyear1(10)}!") # Nextyear1(10)类普通属性方法,类外部的函数可以通过类进行调用
Weijp = people("Weijp", 39, 144) # 用poeple类实例化(创建)一个具体的对象Weijp,并把参数值"Weijp", 18, 144分别传给对象
# 传值完之后,实例对象的weight参数值就被重写覆盖了,由100改写城144
Weijp.speak("english") # 调用对象Weijp的spesk()方法,并把参数值"english"传给spesk()方法,然后执行
print(f"My name is {Weijp.name}!") # Weijp.name是实例普通属性变量,类外部的函数是可以访问的
print(f"My name is {Weijp.na}!") # 类的类变量在实例化时也可以被传给对象
# print(f"Next year,my weight will be {Weijp.Nextyear1(10)}!") # 报错,因为Nextyear1()是类的方法,实例过程,这个方法并没有传递给实例对象
# 即实例对象Weijp并没有这个方法,所以就不存在被调用了
# print(f"My age is {Weijp.__age}!") # 报错,因为Weijp.__age被定义是私有属性的实例变量,只允许类里面的函数访问,类外的外部函数无法访问
print(f"My age is {Weijp._people__age}!") # 可以通过 对象._类名+属性 方式变量操作后再访问,只是不能直接访问而以了
# print(f"Next year,my weight will be {Weijp.__Nextyear2(10)}!") # 报错,因为__Nextyear()被定义为私有属性方法,类外的外部函数无法调用
# 类的方法有两种:类的方法和类的实例方法
# 类的方法:定义在类里,参数没有self,(如上面的Nextyear1(d)方法)通过类名称进行调用,但在实例化时不会将该方法传给对象;
# 类的实例方法:定义在类里,第一个参数是self,(speak(self, lw)方法)是通过的对象的名称进行调用,在实例化时会将该方法传给对象;
##############关于类的继承的案例
print('+'*30)
class Student(people):
name = 'Sdudent' # 类公有属性变量可以重写,覆盖父类的name
__MinIQ = 100 # 类私有属性变量可以重写,覆盖父类的__MinIQ
def __init__(self, n, a, w, h): # 重写__init__()函数,对父类的__init__()做局部修改
super().__init__(n, a, w) # 利用super()函数调用父类的__init__()函数,可理解为把父类的n, a, w属性重写一遍
self.height = h # 新增的属性参数
def speak(self, lw): # 重写speak(lw)函数,对父类的speak(lw)做局部修改
super().speak(lw) # 利用super()函数调用父类的speak(lw)函数,可理解为把父类的speak(lw)方法重写一遍
print("Stdudent are veyt clerver! IQ>{}!".format(self.__MinIQ)) # 修改speak(lw),增加功能
# print("My age is {}!".format(self.__age)) # 修改speak(lw),增加功能
def study(self):
print("The students are studying every day!")
print(Student.name) # 继承之后,类公有性变量name被重写为Sdudent,执行结果;Sdudent
print(Student.Nextyear1(5)) # Nextyear1()是类方法,在实例化过程并没有传给实例对象,它只通过类调用;
# Student的类变量weight继承了父类的类变量weight=100,执行结果=100-5=95
Wyt = Student('Wyt',6, 46, 1.2)
print(Wyt.name)
print(Wyt.height)
Wyt.speak('Chinese')
3. 多态:顾名思义,多态就是多种形态!
####### 封装和继承基本上网上都有,但“多态”讲得好而完整的不多,在这里重点讲一下!!!#######
3.1 继承的方法重写方法新增:
3.1.1 前述:要讲多态,得先讲从“方法重写”与“方法新增”讲起,因为如果类没有这两个特性,其实就不存在多态了;
3.1.2 方法重写
3.1.2.1 重定的概念:
- 我们在定义一个类时,定义了类变量a,但是实例对象需要修改这个变量a,我们可以在实例变量中重新定义这个变量,让其覆盖类变量,这个过程叫属性的覆盖(override),也称为属性的重写;
- 如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。在这里,我们主要讲这一点;
3.1.2.2 举例:当子类(B1、B2、B3、...Bn)继承了父类A,那么B1~n就继承了A的所有的方法,当我们调用子类B1所继承A的一个函数fn1( )时,发现跟我们想的效果就一点偏差,如果把父类的这个函数fu1( )修改了,可能影响到其它调整它的程序,这个时候,我们可以在子类B1重新定义一个同名的函数fn1( ),让其覆盖父类的fn1( ),B1类下这个函数虽然名字相同,但是功能却不同,然后我们再调用B1对象的fn1( )函数,去实现我们想要的效果;
3.1.3 方法新增
3.1.3.1 概念:如果从父类继承的方法不能满足子类的需求,可以在子类新增定义一个方法,这个过程叫方法的新增;
3.1.3.2 举例:当子类(B1、B2、B3、...Bn)继承了父类A,那么B1~n就继承了A的所有的方法,当我们调用子类B1所继承A的一个函数fn1( )时,发现还有需另外一个功能函数fn2(),如果到父类中去增加这个函数,对其它调整它的程序完全没有用,纯粹是浪费还可以有其它影响,这个时候,我们可以在子类B1新增定义一个同名的函数fn2( ),再调用B1对象的fn2( )函数去实现我们的需求;
3.1.4 类在继承过程中为适用各种场景的需要而呈现出变化(方法重写和方法新增)的特点,也从另一角度展示了类的“多态”;
3.2 类的多态:目的是接口重用。即:通过一个固定的接口即得到多种形态的结果!
3.2.1 自然界中的多态:我们还是拿社会中的一个例子来说明这个概念吧
3.2.1.1 举例:某个饭馆的厨师分有粤菜厨师、川菜厨师,湘菜厨师,当客户点粤菜时,服务小妹就去粤菜厨师下单,当客户点川菜时,服务小妹就找川菜厨师下单,当客户点湘菜时,服务小妹就找湘菜厨师下单,有时候有些叼酸的客户三个菜系都点,服务小妹就得挨个挨个找各个菜系厨师下单,老板发现这样太麻烦了,还容易出错,既然粤菜厨师、川菜厨师,湘菜厨师都是厨师,就他们归为“厨师”类吧,然后再找个“总厨”统一管理“厨师”类这群人(主要是下单分配),接着再给这个“总厨”定个规则:服务小妹只传菜单给你,什么菜该下单给哪类厨师,你自己按菜单分配,并把菜单传递给各菜系的厨师。这样不管客户点什么菜,服务小妹只管传单就可以了,然后,总厨什么菜系的菜单都可以接(以前粤菜厨师只接粤菜单,川菜厨师只接川菜单,湘菜厨师只接湘菜单)。后来客户对东北菜、鲁菜要求越来越多,于是老板决定增加一类东北菜厨师,鲁菜厨师,然后跟总厨说:北菜厨师,鲁菜厨师都是“厨师”类,他们也归你管。对于厨房增加的各菜系厨师,服务小妹完全不用管,她只客传菜单就可以了。这种通过总厨及规则,总厨可以接服务小妹各种菜系的菜单,然后安排各菜系厨师炒各个系的菜,这种通过总厨实现接单的多样性与出菜的多样性,就是我们自然界一个“多态”例子;
3.2.1.2 上面这个例子能实现“多态”的前提条件:
共性与归类:每个菜系厨师都有“炒菜手艺”,并把个菜系的厨师都归为“厨师”类;
个性:每个菜系厨师的“炒菜手艺”各不相同;
设岗与定规则:设一个“总厨”岗位统一管理,并给这个岗位制定“规则”;
3.2.2 python中类的多态:
3.2.2.1 常见场景:
- 子类继承父类后,子类重写了父类的方法或新增其它的方法;
- 为不同需求定义了多类,这几个类有同名但不同功能的方法,为了根据不同情况调用不同方法而主动让这些类去继承某个类,成为这个类的子类;
3.2.2.2 类能呈现“多态”的前提条件: - 子类继承父类,有同名方法;
- 同名方法功能不同;
- 定义一个参数为类对象的函数为接口,由这个接口来完成各类的方法的调用;
- 类具备了以上的三个前提条件后,我们就可以通过这个接口向类传各种各样的值和调用执行各个方法,从而达到了“接口重用”的目的;
3.2.2.3 类的“多态”给我们带来的好处: - 参数类型多形态:不论对象有多少中形态,只通过这个参数为类的的函数进行调用,然后对应地执行各个方法;
- 对扩展开放:可以增加子类并重写方法,但还依旧利用这个参数为类的的函数进行调用;
类的三个特性的案例:案例详见:ex41d.py
class F1: # 定义一个父类F1
def show(self): # 定义一个方法show
print("F1.show") # 现实一个特定的功能
class S1(F1): # 定义一个子类S1,并让S1类继承F1类
def show(self): # 方法重写,重新定义show函数,让其覆盖父类的show函数
print("RecoverF1,S1.show") # 重写的show函数,现实另一个新的功能
class S2(F1): # 与s1同
def show(self):
print("RecoverF1,S2.show")
class F2: # 定义一个全新的F2类,它是一个独立的类,但一样可以通过Func函数调用
def show(self): # 定义一个方法show,其与上面的show函数同名
print("F2.show") # 全新的show函数,其有与上面不同的功能
F1_obj = F1() # 实例化
F2_obj = F2() # 实例化
s1_obj = S1() # 实例化
s2_obj = S2() # 实例化
# 多接口方式:这种方式,在编程是,使用者得先知道他要执行哪个功能的show,然后定义这个show,再调用这个show,
# 这样,他的程序里会存在好多个接口
F1_obj.show() # 调用F1_obj对象的show()方法
F2_obj.show() # 调用F2_obj对象的show()方法
s1_obj.show() # 调用s1_obj对象的show()方法
s2_obj.show() # 调用s2_obj对象的show()方法
# 统一接口方式:这种方式,使用者只管调用Func这个统一的接口,代码写好后不用再做调整
# 需要什么talk,只要在类下定义一个show就可以了,然后给Func传想的show的对象即可,而接口接依然只是Func
def Func(obj): # Func函数需要接收一个类的对象(这个类必须有show()方法),实现接口的统一
obj.show() # Func函数的功能就调用对象的show()方法
Func(F1_obj) # 在Func函数中传入F1类的对象 F1_obj,执行 F1 的show方法
Func(F2_obj) # 在Func函数中传入F2类的对象 F2_obj,执行 F2 的show方法
Func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法
Func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法