什么是继承
# 面向对象的三大特性之一 —— 继承
# 继承 :至少两个类 : 什么 是 什么 的关系,为了避免几个类之间有相同的代码
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类
class ParentClass1: #定义父类 pass class ParentClass2: #定义父类 pass class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类 pass
例子:
class Animal: def __init__(self,name,aggressivity,life_value): self.name = name self.aggressivity = aggressivity self.life_value = life_value #狗 class Dog(Animal): # 定义一个狗类 def bite(self,people): people.life_value -= self.aggressivity #人 class Person(Animal): # 定义一个人类 def attack(self,dog): dog.life_value -= self.aggressivity huang = Dog('大黄',100,3000) #__init__ 找父类 print(huang.life_value) boss_gold = Person('金老板',5,250) #__init__ 自己没有 找父类 print(boss_gold.life_value)
查看继承:
print(Dog.__bases__)
print(Animal.__bases__)
在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时
我们不可能从头开始写一个类B,这就用到了类的继承的概念。
通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用
继承:把相同的代码放在父类中,子类的对象在子类中没有找到方法的时候,使用父类的
当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。
例子:
class Animal: def __init__(self, name, aggressivity, life_value): self.name = name # 人和狗都有自己的昵称; self.aggressivity = aggressivity # 人和狗都有自己的攻击力; self.life_value = life_value # 人和狗都有自己的生命值; def eat(self): self.life_value += 10 class Dog(Animal): def __init__(self, name, breed, aggressivity, life_value): Animal.__init__(self, name, aggressivity, life_value) self.breed = breed #派生属性 def bite(self, people): #派生方法:狗有咬人的技能 people.life_value -= self.aggressivity class Person(Animal): def __init__(self,name, aggressivity, life_value, money): Animal.__init__(self, name, aggressivity, life_value) self.money = money #派生属性 def attack(self, dog): #派生方法:人有攻击的技能 dog.life_value -= self.aggressivity egg = Person('egon',10,1000, 100) snoopy = Dog('太白', "京巴", 50, 1000) print(snoopy.life_value) print(egg.attack(snoopy)) print(snoopy.life_value) snoopy.eat() print(snoopy.life_value)
派生
派生属性:父类没有的属性
派生方法 :父类没有的方法
#派生属性 : 在自己的init方法里 使用父类的init方法 —— 指名道姓调用方法
#派生方法 : 在子类中增加父类没有的
class Animal: def __init__(self, name, aggressivity, life_value): self.name = name # 人和狗都有自己的昵称; self.aggressivity = aggressivity # 人和狗都有自己的攻击力; self.life_value = life_value # 人和狗都有自己的生命值; def eat(self): self.life_value += 10 class Dog(Animal): def __init__(self, name, breed, aggressivity, life_value): Animal.__init__(self, name, aggressivity, life_value) self.breed = breed #派生属性 def bite(self, people): #派生方法:狗有咬人的技能 people.life_value -= self.aggressivity def eat(self): # Animal.eat(snoopy) print("dog is eating") class Person(Animal): def __init__(self,name, aggressivity, life_value, money): Animal.__init__(self, name, aggressivity, life_value) self.money = money #派生属性 def attack(self, dog): #派生方法:人有攻击的技能 dog.life_value -= self.aggressivity egg = Person('egon',10,1000, 100) snoopy = Dog('太白', "京巴", 50, 1000) print(snoopy.life_value) print(egg.attack(snoopy)) print(snoopy.life_value) Animal.eat(snoopy) print(snoopy.life_value)
父类没有 子类有: 子类
子类没有 父类有: 父类
父类有 子类有: 子类
父类有 子类有: 想用父类的 -- 父类名.方法名(子类对象名)
父类有 子类有: 父类子类都想用 -- 在子类中指名道姓的调用父类的方法
概括: 只要子类有,就用子类的
只要想用父类的,父类名.父类方法名(子类对象名)
在新式类中,super的用法
class Animal: def __init__(self, name, aggressivity, life_value): self.name = name # 人和狗都有自己的昵称; self.aggressivity = aggressivity # 人和狗都有自己的攻击力; self.life_value = life_value # 人和狗都有自己的生命值; def eat(self): self.life_value += 10 class Dog(Animal): def __init__(self, name, breed, aggressivity, life_value): # Animal.__init__(self, name, aggressivity, life_value) # super(Dog, self).__init__(name, aggressivity, life_value) super().__init__(name, aggressivity, life_value) self.breed = breed #派生属性 def bite(self, people): #派生方法:狗有咬人的技能 people.life_value -= self.aggressivity def eat(self): # Animal.eat(snoopy) super().eat() print("dog is eating") class Person(Animal): def __init__(self,name, aggressivity, life_value, money): # Animal.__init__(self, name, aggressivity, life_value) # super(Person,self).__init__(name, aggressivity, life_value) super().__init__(name, aggressivity, life_value) self.money = money #派生属性 def attack(self, dog): #派生方法:人有攻击的技能 dog.life_value -= self.aggressivity egg = Person('egon',10,1000, 100) snoopy = Dog('太白', "京巴", 50, 1000) print(snoopy.life_value) print(egg.attack(snoopy)) print(snoopy.life_value) Animal.eat(snoopy) print(snoopy.life_value) super(Dog,snoopy).eat() #Animal.eat(snoopy) print(snoopy.life_value)
#用子类的对象,调用父类的方法:
#如果子类中没有这个方法,直接就使用父类的
#如果子类中有同名方法:
# 经典类 指名道姓 父类名.父类方法名(子类对象名) 类内外一致
# 新式类 super方法 super(子类名,子类对象).方法名() 类内可以省略super的参数
在python3中,子类执行父类的方法也可以直接用super方法.
class A: def hahaha(self): print('A') class B(A): def hahaha(self): super().hahaha() #在B中调用A的方法 #super(B,self).hahaha() #A.hahaha(self) print('B') a = A() b = B() b.hahaha() super(B,b).hahaha() #B类中的b调用超类A中的哈哈哈方法
面试题:
class Foo: def __init__(self): self.func() def func(self): print('Foo.func') class Son(Foo): def func(self): print('Son.func') s = Son() 打印出来是: Son.func
在Python2中既有经典类,又有新式类
class A:pass #经典类
class A(object): pass #新式类
Python3中只有新式类:
#经典类和新式类的多继承问题,继承顺序问题
#经典类 : 博大精深 所以经典类就是深度优先
#新式类 :广度优先
#coding:utf-8 class F(object): pass def f(self): print('F') class E(F): pass def f(self): print('E') class D(F): pass # def f(self): # print('D') class B(D): pass # def f(self): # print('B') class C(E): pass def f(self): print('C') class A(B,C): pass # def f(self): # print('A') a = A() a.f()
print(A.mro()) #新式类:查看继承顺序 广度优先顺序
# class A(object):pass #新式类
#新式类继承顺序:F->D->B->E->C->A
#经典类继承顺序:F->D->B->A->E->C
#python3中统一都是新式类
#pyhon2中才分新式类与经典类
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
# 在好多个有继承关系的类里面,找一个方法,找的顺序问题
# 继承三层
# py3 —— 广度优先
# py2 —— 新式类
#面试 —— 能对应 新式类 是广度优先 经典类是深度优先
继承的作用
减少代码的重用 提高代码可读性 规范编程模式
几个名词
抽象:抽象即抽取类似或者说比较像的部分。是一个从具题到抽象的过程。
继承:子类继承了父类的方法和属性
派生:子类在父类方法和属性的基础上产生了新的方法和属性