面向对象之继承与派生
一、继承:
1. 什么是继承?
在程序中继承是一种新建子类的方式,新创建的类称之为子类\派生类,被继承的类称之为父类\基类\超类 继承描述的是一种遗传关系,子类可以重用父类的属性。
2. 为何用继承?
减少类与类之间代码冗余的问题。
3. 如何继承?
先抽象再继承。
在python中继承的特点:分为单继承&多继承
# class Parent1(object): # pass # # class Parent2: # pass # # class Sub1(Parent1): # pass # # class Sub2(Parent1,Parent2): # pass # # print(Sub1.__bases__) #(<class '__main__.Parent1'>,) # print(Sub2.__bases__) #(<class '__main__.Parent1'>, <class '__main__.Parent2'>) # # print(Parent1.__bases__) #(<class 'object'>,) # print(Parent2.__bases__) #(<class 'object'>,)
# x=2 # class Foo: # # x=1 # pass # obj=Foo() # # obj.x=3 # print(obj.x) #3
注意:
python2与python3在继承上的区别?
新式类:但凡继承object类的子类,以及该子类的子子类,…都称之为新式类
经典类:没有继承object类的子类,以及该子类的子子类,…都称之为经典类
只有在python2中才区分新式类与经典类,在python3中,定义类时会默认继承object类,只是没有写出来。
基于类的继承 来解决代码冗余的实例:
class People: school = 'xxx' def __init__(self, name, age, gender): self.name = name self.age = age self.gender = gender # print(People.__init__) class Student(People): def __init__(self, name, age, gender): self.name = name self.age = age self.gender = gender def choose_course(self): print('%s is choosing course' %self.name) class Teacher(People): # tea, 'egon', 18, 'male', 10, 3000 def __init__(self, name, age, gender,level,salary): # self.name = name # self.age = age # self.gender = gender People.__init__(self, name, age, gender) self.level=level self.salary=salary def score(self,stu,num): stu.num=num print('老师%s给学生%s打分%s' %(self.name,stu.name,num)) # stu=Student('kevin',38,'male') #__init___(stu1,'kevin',38,'male') # print(stu.__dict__) tea=Teacher('egon',18,'male',10,3000) #__init___(tea,'egon',18,'male',10,3000) print(tea.__dict__) # print(stu.school) # print(tea.school)
class People: # school = 'xxx' # def __init__(self, name, age, gender): # self.name = name # self.age = age # self.gender = gender # # class Teacher(People): # # tea, 'egon', 18, 'male', 10, 3000 # def __init__(self, name, age, gender,level,salary): # People.__init__(self, name, age, gender) # # super(Teacher,self).__init__(name, age, gender) # # self.level=level # self.salary=salary # # def score(self,stu,num): # stu.num=num # print('老师%s给学生%s打分%s' %(self.name,stu.name,num)) # # tea=Teacher('egon',18,'male',10,3000) #__init___(tea,'egon',18,'male',10,3000) # print(tea.__dict__) # # print(stu.school)
在子类派生出的新功能中如何重用父类的功能:
方式一: 指名道姓地访问某一个类中的函数,与继承无关
方式二:super(Teacher,self),在python3中super可以不传参数,调用该函数会得到一个特殊的对象,该对象是专门用来访问父类中属性,
强调:super会严格参照类的mro列表依次查找属性
class A: def test(self): print('A.test') super().test() class B: def test(self): print('from B') class C(A,B): pass c=C() c.test() print(C.mro())
输出
A.test from B [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
在单继承背景下的属性查找:无论是新式类还是经典类属性查找顺序都一样,先obj->类->父类->…
# class Foo: # def f1(self): # print('Foo.f1') # # def f2(self): # print('Foo.f2') # self.f1() #obj.f1() # # class Bar(Foo): # def f1(self): # print('Bar.f1') # # obj=Bar() # obj.f2()
输出
Foo.f2
Bar.f1
在多继承背景下的属性查找:如果一个子类继承了多个分支,但是多个分支没有汇聚到一个非object类,无论是新式类还是经典类属性查找顺序都一样:会按照从左到右的顺序一个分支一个分支的查找下去。
class E: xxx='E' pass class F: xxx='F' pass class B(E): xxx='B' pass class C(F): xxx='C' pass class D: xxx='D' pass class A(B,C,D): xxx='A' pass obj=A() obj.xxx=111 print(obj.xxx) print(A.mro())
输出
111 [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class 'object'>]
在多继承背景下,如果一个子类继承了多个分支,但是多个分支最终汇聚到一个非object类(菱形继承问题)
新式类:广度优先查找:obj->A->B->E->C->F->D->G->object
经典类:深度优先查找:obj->A->B->E->G->C->F->D
class G: xxx='G' class E(G): xxx='E' pass class F(G): xxx='F' pass class B(E): xxx='B' pass class C(F): xxx='C' pass class D(G): xxx='D' pass class A(B,C,D): xxx='A' pass print(A.mro())
输出
[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <class 'object'>]
二、派生
在理解完继承之后,就很容易理解派生了。因为子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。
class Hero: def __init__(self,nickname,aggressivity,life_value): self.nickname=nickname self.aggressivity=aggressivity self.life_value=life_value def move_forward(self): print('%s move forward' %self.nickname) def move_backward(self): print('%s move backward' %self.nickname) def move_left(self): print('%s move forward' %self.nickname) def move_right(self): print('%s move forward' %self.nickname) def attack(self,enemy): enemy.life_value-=self.aggressivity class Garen(Hero): pass class Riven(Hero): pass g1=Garen('草丛伦',100,300) r1=Riven('锐雯雯',57,200) print(g1.life_value) r1.attack(g1) print(g1.life_value) 输出
300
240
class Riven(Hero): camp='Noxus' def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类 print('from riven') def fly(self): #在自己这里定义新的 print('%s is flying' %self.nickname)
在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要为其传值
class Riven(Hero): camp='Noxus' def __init__(self,nickname,aggressivity,life_value,skin): Hero.__init__(self,nickname,aggressivity,life_value) #调用父类功能 self.skin=skin #新属性 def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类 Hero.attack(self,enemy) #调用功能 print('from riven') def fly(self): #在自己这里定义新的 print('%s is flying' %self.nickname) r1=Riven('锐雯雯',57,200,'比基尼') r1.fly() print(r1.skin) ''' 运行结果 锐雯雯 is flying 比基尼 '''