任性插入:
继承的时候,如果子类中没有初始化函数,但是要去看看父类中有没有初始化函数,再决定子类在实例化的时候要不要传参;
子类中的方法想要调用父类中的方法,self.方法名;
子类中的方法想使用类中的其他方法也是加上self.;
1.面向对象三大特性:
封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中;
继承 实现代码的重用,相同的代码不需要重复的编写;
多态 不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度;
补充:
【转自别人】
继承好处:
提升代码的重用率:
有利于代码的管理,在需要改代码的时候,只需要在父类中修改,不需要动子类中的代码。
单继承和多继承(但是在java中继承只能是单继承,其余的语言不清楚)
classFather():pass
classMather():pass
class Son(Father):##########单继承
pass
class Daughter(Father,Mather):##########多继承
pass
View Code
2.什么时候用组合,什么时候用继承?
(1)当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好;
比如说一架车子是由不同的组件组合起来的,这样类似的即用组合,把全部的零件组装成一个完整的整体。
(2)当类之间有很多相同的功能,把这些共同的功能写成父类,子类去继承就可以,用继承比较好;
比如小猫小狗人都有吃喝拉睡的动作,如果在各自的类中都要写吃喝拉睡的方法,那重复的代码太多,那就可以使用继承,写个动物类中有吃喝拉睡,小猫小狗人调用即可。
3.继承的作用:
(1)减少重复代码,子类可扩展或者可以修改【但是往往在实践中,该含义意义并不很大,甚至常常是有害的,因为它使得子类与基类出现强耦合。】
(2)声明某个子类兼容于某基类,定义一个接口类,子类继承接口类,并且在子类实现接口中定义的方法————————————接口继承
【目的:规范子类,子类实现接口类的中定义的方法,并且不必要实例化】
【copy来的:接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,
可一视同仁的处理实现了特定接口的所有对象”一这在程序设计上, 叫做归一化。】————个人感觉意思就是:任何一个子类要调用接口类都必须要实现其定义的方法,
子类想怎么实现就怎么实现,不关其他任何人的事情,当然子类还可以有属于自己的方法【类似java中的接口。。。。】。
4.继承的MRO顺序:
在经典类中,没有继承object类
在新式类中,有继承object类
在python3中,所有类最顶层父类都是object类,与java类似,如果定义类的时候没有写出父类,则object类就是其直接父类。
现在的问题是,如果多个父类中包含了同名的方法,此时会发生什么呢?此时排在前面的父类中的方法会“遮蔽”排在后面的父类中的同名方法。
class Father(object):#######新式类
pass
class Mather():##########经典类
pass
View Code
但是,这一切在python3出现之后发生了变化,因为在python3里面创建的都是新式类。
不管它俩的变化,我们目前关注的是两种类在多继承状态下查找“方法”的顺序。
经典类:深度查找
新式类:广度查找
经典类顺序(深度):F---D---B--A后F---E---C---A
新式类顺序(广度):F--D---B(不找基类)后F---E--C--A
但是在新式类中,对于程序猿定义的每一个类,python会计 算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表
为了实现继承.python会在MRO列表上从左到右开始查找基类,当到找到第一个匹配这个属生的类为止,
而这个MRO列表的构造是通过一个C3线性化算法来实现的。它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
(1)子类会先于父类被检查【类似作用域】
(2)多个父类会根据它们在列表中的顺序被检查
(3)如果对下一个类存在两个合法的选择,选择第一个父类;
新式类可以直接通过 类名.__mro__ 的方式获取类的 MRO,也可以通过 类名.mro() 的形式。
classFather():print("这个是父亲")classMather():print("这个是母亲")class Son(Father):##########单继承
print("这个是儿子")class Son1(Father,Mather):##########多继承
print("这个是儿子")class Daughter(Father,Mather):##########多继承
print("这个是女儿")print(Son1.__mro__)print(Daughter.__mro__)###################
这个是父亲
这个是母亲
这个是儿子
这个是儿子
这个是女儿
(, , , )
(, , , )
View Code
重写:
子类包含与父类同名的方法的现象被称为方法重写(Override),也被称为方法覆盖。可以说子类重写了父类的方法,也可以说子类覆盖了父类的方法。
通过使用未绑定方法即可在子类中再次调用父类中被重写的方法
【在通过类名调用实例方法时,Python 不会为实例方法的第一个参数 self 自动绑定参数值,而是需要程序显式绑定第一个参数 self。这种机制被称为未绑定方法。】
classFruit():defname(self):print('我是所有水果的父类')classApple(Fruit):defname(self):print('我重写了Fruit的name方法——苹果')defrun_fruit(self):#直接执行name方法,将会掉哟个子类重写之后的name方法
self.name()#使用类名调用实例方法(未绑定方法)调用父类被重写的方法
Fruit.name(self)
apple=Apple()
apple.name()
apple.run_fruit()##########################
我重写了Fruit的name方法——苹果
我重写了Fruit的name方法——苹果
我是所有水果的父类
View Code
5.接口继承
接口是一组功能的入口,要调用某一组功能,需要通过接口来进行调用,而不需要关注这组功能是如何实现的,要的只是结果。
在类里,接口是提取了一群类共同的函数,可以把接口当做一个函数的集合。
正如2所说的,接口父类只是定义有什么方法,但是并不具体实现其方法体,当有子类去继承该父类时候,需要具体去实现这些方法体。
1 classAnimal():2 defeat(self):3 pass
4 defread(self):5 pass
6
7 classCat(Animal):8 defeat(self,name):9 print('%s 在吃鱼' %name)10
11 defread(self,name):12 print('%s 在读书' %name)13 a=Animal()14 print(a.eat())15 c=Cat()16 c.eat("周周")17 c.eat("小周周")18
19 ############
20
21 None22 周周 在吃鱼23 小周周 在吃鱼
View Code
6.抽象类
抽象类的本质上也是类,但是抽象类只能够被继承,不能进行实例化,也就是说可以当父类,但是不能生成对象。
抽象类介于接口和归一化中间,用于实现接口的归一化
当子类继承抽象类的时候,如果抽象类定义了抽象方法,那么子类必须要定义同名的方法。即父类限制:
1、子类必须要有父类的方法
2、子类实现的方法必须跟父类的方法的名字一样
python的抽象类通过abc模块实现。
importabcclass Animal(metaclass=abc.ABCMeta):
@abc.abstractmethoddefeat(self):pass@abc.abstractmethoddefread(self):pass
classCat(Animal):defeat(self,name):print('%s 在吃鱼' %name)defread(self,name):print('%s 在读书' %name)###a=Animal()#####注意注意,不能实例化,否则报错#####TypeError: Can't instantiate abstract class Animal with abstract methods eat, read
c=Cat()
c.eat("周周")
c.eat("小周周")######
周周 在吃鱼
小周周 在吃鱼
View Code
7.super方法调用父类的数据属性和函数属性
Python 要求,如果子类重写了父类的构造方法,那么子类的构造方法必须调用父类的构造方法。
子类的构造方法调用父类的构造方法有两种方式:
使用未绑定方法,这种方式很容易理解。因为构造方法也是实例方法,当然可以通过这种方式来调用。
使用 super() 函数调用父类的构造方法。
【注意,当子类继承多个父类是,super() 函数只能用来调用第一个父类的构造方法,而其它父类的构造方法只能使用未绑定的方式调用。】
在继承中基类的构造(__init__()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用,使用super().__init__()或parentClassName.__init__()或者其他方式。【书写的方式有点点不同】
重写了就只会调用子类中的重写的方法;
'''重写:子类和父类里面有相同的函数名
扩展:子类有父类里面没有的方法'''
classParent():def __init__(self,name,age):
self.name=name
self.age=agedefmsg(self):print('我是父类!!!!')classSon(Parent):def __init__(self,name,age,job):
super().__init__(name,age)
self.job=jobdefmsg(self):print('我是子类============')defjob_msg(self):print('我的名字是:{0},年龄{1},工作是:{2}'.format(self.name,self.age,self.job))
son=Son('jack','25','python测试工程师')
son.msg()
son.job_msg()##########################
我是子类============我的名字是:jack,年龄25,工作是:python测试工程师
View Code
classVehicle1:
Country='China'
def __init__(self,name,speed,load,power):
self.name=name
self.speed=speed
self.load=load
self.power=powerdefrun(self):print('开动啦')print('开动啦')classSubway(Vehicle1):def __init__(self,name,speed,load,power,line):###使用父类的数据属性
#Vehicle.__init__(self,name,speed,load,power)###如果是使用父类的名字,那在修改后,子类的就很麻烦修改
super().__init__(name,speed,load,power)###super就不用修改咯
#super(__class__,self).__init__(name,speed,load,power)###三种方式均可以
#super(Subway,self).__init__(name,speed,load,power)
self.line=linedefshow_info(self):print(self.name,self.speed,self.load,self.power,self.line)defrun(self):###调用父类的方法
#Vehicle.run(self)
super().run()print('%s %s 号线,开动啦' %(self.name,self.line))
line2=Subway('成都地铁','100km/h',1000000000,'电',2)
line2.show_info()
line2.run()print(line13.__class__)########
成都地铁 100km/h 1000000000 电 2开动啦
成都地铁2号线,开动啦
View Code
super()是一个特殊的函数,将父类跟子类关联起来,super()._init_(),,其作用是将子类包含父类的所有属性。
【为避免多继承报错,使用不定长参数,接受参数】
__author__ = 'Administrator'
classParent(object):def __init__(self,name):print("Parent的__init__方法开始!!调用")
self.name=nameprint("Parent的__init__方法结束==调用")classSon1(Parent):def __init__(self,name,age):print("Son1的__init__方法开始调用")
self.age=age
Parent.__init__(self,name)print("Son1的__init__方法结束调用")classSon2(Parent):def __init__(self,name,gender):print("Son2的__init__方法开始调用")
self.gender=gender
Parent.__init__(self,name)print("Son2的__init__方法结束调用")classGrandson(Son1,Son2):def __init__(self,name,age,gender):print("Grandson的__init__方法开始调用")
Son1.__init__(self,name,age)
Son2.__init__(self,name,gender)print("Grandson的__init__方法结束调用")
grandson=Grandson("孙子",18,"男")############################
Grandson的__init__方法开始调用
Son1的__init__方法开始调用
Parent的__init__方法开始!!调用
Parent的__init__方法结束==调用
Son1的__init__方法结束调用
Son2的__init__方法开始调用
Parent的__init__方法开始!!调用
Parent的__init__方法结束==调用
Son2的__init__方法结束调用
Grandson的__init__方法结束调用
View Code
___author__ = 'Administrator'
classParent(object):def __init__(self,name,*args,**kargs):print("Parent的__init__方法开始!!调用")
self.name=nameprint("Parent的__init__方法结束==调用")classSon1(Parent):def __init__(self,name,age,*args,**kargs):print("Son1的__init__方法开始调用")
self.age=age
super().__init__(name,*args,**kargs)print("Son1的__init__方法结束调用")classSon2(Parent):def __init__(self,name,gender,*args,**kargs):print("Son2的__init__方法开始调用")
self.gender=gender
super().__init__(self,name,*args,**kargs)print("Son2的__init__方法结束调用")classGrandson(Son1,Son2):def __init__(self,name,age,gender):print("Grandson的__init__方法开始调用")
super().__init__(name,age,gender)print("Grandson的__init__方法结束调用")
grandson=Grandson("孙子",18,"男")##############################
Grandson的__init__方法开始调用
Son1的__init__方法开始调用
Son2的__init__方法开始调用
Parent的__init__方法开始!!调用
Parent的__init__方法结束==调用
Son2的__init__方法结束调用
Son1的__init__方法结束调用
Grandson的__init__方法结束调用
View Code
___author__ = 'Administrator'
classParent(object):def __init__(self,name,*args,**kargs):print("Parent的__init__方法开始!!调用")
self.name=nameprint("Parent的__init__方法结束==调用")classSon1(Parent):def __init__(self,name,age,*args,**kargs):print("Son1的__init__方法开始调用")
self.age=age
super().__init__(name,*args,**kargs)print("Son1的__init__方法结束调用")classSon2(Parent):def __init__(self,name,gender,*args,**kargs):print("Son2的__init__方法开始调用")
self.gender=gender
super().__init__(self,name,*args,**kargs)print("Son2的__init__方法结束调用")classGrandson(Son1,Son2):def __init__(self,name,age,gender):print("Grandson的__init__方法开始调用")
super().__init__(name,age,gender)print("Grandson的__init__方法结束调用")print(Grandson.__mro__)
grandson=Grandson("孙子",18,"男")#################################
(, , , , )
Grandson的__init__方法开始调用
Son1的__init__方法开始调用
Son2的__init__方法开始调用
Parent的__init__方法开始!!调用
Parent的__init__方法结束==调用
Son2的__init__方法结束调用
Son1的__init__方法结束调用
Grandson的__init__方法结束调用
View Code
通过父类名调用的时候,parent调用了两次;
使用super的时候,parent 只是执行了一次;
___author__ = 'Administrator'
classParent(object):def __init__(self,name):print("Parent的__init__方法开始!!调用")
self.name=nameprint("Parent的__init__方法结束==调用")classSon(Parent):def __init__(self,name,age):print("Son的__init__方法开始调用")
self.age=age
super().__init__(name)#单继承不能提供全部参数
print("Son的__init__方法结束调用")classGrandson(Son):def __init__(self,name,age,gender):print("Grandson的__init__方法开始调用")
super().__init__(name,age)#单继承不能提供全部参数
print("Grandson的__init__方法结束调用")
grandson=Grandson("孙子",18,"男")###########################
Grandson的__init__方法开始调用
Son的__init__方法开始调用
Parent的__init__方法开始!!调用
Parent的__init__方法结束==调用
Son的__init__方法结束调用
Grandson的__init__方法结束调用
View Code
【转自别人:】
总结:
super().__init__相对于类名.__init__,在单继承上用法基本无差;
但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次,具体看前面的输出结果;
多继承时,使用super方法,对父类的传参数,应该是由于python中super的算法导致的原因,必须把参数全部传递,否则会报错;
单继承时,使用super方法,则不能全部传递,只能传父类方法所需的参数,否则会报错,
多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍, 而使用super方法,只需写一句话便执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因。
不知道是不是重复了:具有两个父类的属性和方法,如果两个父类具有同名方法的时候,就近原则(__init__也是)。
8.
在调用父类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。区别于在类中调用普通函数时并不需要带上self参数;
重写父类方法——————————对于父类的方法,如果有不符合子类想要做的事情,可以对父类的方法进行重写,重写时要与父类的方法同名,这样python将不会考虑父类的方法,而只是关注子类定义的相应的方法。
9.
isinstance(obj,cls)判断是否obj是否是类 cls 的对象
issubclass(sub, super)判断sub类是否是 super 类的派生类
1 classPerson():2 pass
3 classMan(Person):4 pass
5 p=Person()6 m=Man()7 print(isinstance(p,Person))8 print(isinstance(m,Person))#####因为有了继承的关系
9 print(isinstance(p,Man))10 print(issubclass(Man,Person))11
12 ######################
13True14True15False16 True
View Code
10.多态:同一个方法在不同的类中最终呈现出不同的效果,即为多态。
classFruit():defname(self):print('我是水果')classApple(Fruit):defname(self):print('我是苹果')
Fruit().name()
Apple().name()
View Code