20181214(继承,派生)
派生/继承/继承结构
派生描述了子类衍生出新的特性,新类保留已存类类型中所有需要的数据和行为,但允许修改或者其它的自定义操作,都不会修改原类的定义。 继承描述了子类属性从祖先类继承这样一种方式 继承结构表示多“代”派生,可以述成一个“族谱”,连续的子类,与祖先类都有关系。
一、继承
1、定义:继承是一种类与类之间的关系,是一种新建类的的方式,在python中支持一个儿子继承多个爹,即多继承。
新建的类称为子类或者派生类, 父类又可以称为基类或者超类。
子类会”遗传“父类的属性
2、使用继承的好处:减少代码冗余
3、使用继承的方法:
class Father: # 定义一个父类
pass
class FatherWang: # 定义另一个父类
pass
# 在子类中,类名后面括号内写上父类的名称即可
# 在python中,存在多个父类时,括号内用逗号隔开即可。其他语言不支持这个操作
class Son1(Father): # 单继承,基类是Father,派生类是Son1
pass
class Son2(Father,FatherWang): #子类继承了两个父类
pass
# 子类可以使用父类的属性和函数
# 如果子类中有,优先从子类中拿取,子类中没有,就从父类中拿取
class Parent:
year = 2018
def coding(self):
print("coding...")
class Sub(Parent): # Parent的子类Sub
year = 2019
print(Parent.year) # 注意调取参数的语法
print(Sub.year)
s = Sub() # 先从类中创建一个对象
print(s.year) # 对象优先从其类中查找数据,没有的去父类中寻找
s.coding()
输出结果:
2018
2019
2019
coding...
4、查看继承
__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类.
交互模式下输入:
>>> class Father:pass
...
>>> class FatherWang:pass
...
>>> class Son(Father,FatherWang): pass
...
>>> Son.__bases__
(<class '__main__.Father'>, <class '__main__.FatherWang'>)
>>> Son.__base__
<class '__main__.Father'>
二、继承与抽象
通过继承,避免了重复代码的编写 #从父类中继承得到了一些基础属性 通过抽象,避免了继承到一些不该有的内容 #抽象即抽取类似部分或者比较像的部分。 应该先抽象再具体
(继承是拿到父类的属性,如果想要修改也可以自己修改;抽象就是尽可能求同,找出普适性的共同点,将其通过描述(定义变量或函数)确定下来(父类))
三、派生
派生:子类定义自己新的属性,如果与父类同名,以子类自己的为准。派生类是子类的一种,添加、修改了自己独有的属性的子类就是派生类。
在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要调用父类中重名的那个函数功能。此时用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要为其传值。
派生与覆盖:
子类出现与父类重复的名字 称之为覆盖 子类出现与父类不重复的名字 称之为派生
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
class Student(Person): # 这是派生,有自己的内容
def __init__(self,name,age,number):
Person.__init__(self,name,age)
self.number = number
class Student1(Person): # 这不是派生,因为没有自己的内容
pass
四、子类访问父类中的方法
指名道姓调用函数:其实与继承没有什么关系。
super函数:super().__init__(name,age) 创建一个特殊的对象,用于调用父类的方法。
在python2 中,需要传入当前类和当前对象,super(Student,self).__init__(name,age,sex)
两种方法的区别:方式一其实和继承没有关系,方式二super()是依赖于继承的,即使没有继承关系,也会按照mro算法继续向后查找。
方法一:注意传参方式
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
class Student(Person):
def __init__(self,name,age,number):
Person.__init__(self,name,age) # 直接调用Person中的函数
self.number = number # Person中没有的则需要子类自己处理
sut = Student("alex",33,110)
print(sut.__dict__)
方法二:注意传参方式
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
class Student(Person):
def __init__(self,name,age,number):
super().__init__(name,age) # 此处传参没有self!
self.number = number
sut = Student("alex",33,110)
print(sut.__dict__)
两种方法的区别:
#指名道姓
class A:
def __init__(self):
print('A的构造方法')
class B(A):
def __init__(self):
print('B的构造方法')
A.__init__(self)
class C(A):
def __init__(self):
print('C的构造方法')
A.__init__(self)
class D(B,C):
def __init__(self):
print('D的构造方法')
B.__init__(self)
C.__init__(self)
pass
f1=D() #A.__init__被重复调用
'''
D的构造方法
B的构造方法
A的构造方法
C的构造方法
A的构造方法
'''
#使用super()
class A:
def __init__(self):
print('A的构造方法')
class B(A):
def __init__(self):
print('B的构造方法')
super(B,self).__init__()
class C(A):
def __init__(self):
print('C的构造方法')
super(C,self).__init__()
class D(B,C):
def __init__(self):
print('D的构造方法')
super(D,self).__init__()
f1=D() #super()会基于mro列表查找,mro是一种算法,具体可以谷歌。
'''
D的构造方法
B的构造方法
C的构造方法
A的构造方法
'''
五、存在继承关系后的属性查找
先找对象,然后找类,然后找父类,然后是父类的父类(沿着继承关系)。。。直到报错
class Foo:
def f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
self.f1()
class Bar(Foo):
def f1(self):
print('Bar.f1')
b=Bar()
b.f2()
输出结果:
Foo.f2
Bar.f1 #self为b,所以是b.f1(),Bar().f1()
当存在多个父类时,按照继承顺序,从左到右,依次查找
class A:
a=1
class B:
a=2
class C(A,B):
print(a)
输出结果为:
1
六、经典类与新式类
1.只有在python2中才分新式类和经典类,python3中统一都是新式类
2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
4.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
object称之为根类 ,意为所有类都源自于object类。
七、菱形继承
存在共同父类时,在菱形继承中,会先深度再广度继承
如果在父类中,既有菱形又有非菱形,先按照继承顺序查找,碰到菱形时 就先广度再深度。