DAY18 面向对象三大特性之继承
继承
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类。父类又被称为超类,基类;新建的类称为子类或者派生类。
在python3.x版本中,所有的类都默认继承object类。
1.继承与重用
首先,我们来看看一个例子:
# 猫类:吃喝睡,爬树 # 狗类:吃喝睡,看家 #一。通过之前对面向对象的学习,我们可以轻易的定义出这两个类来: class Cat: def __init__(self,name,kind,food): self.name = name self.kind = kind self.food = food def eat(self): print('%s吃%s'%(self.name,self.food)) def drink(self): print('%s喝水'%self.name) def sleep(self): print('%s在睡觉'%self.name) def climb(self): print('%s在爬树'%self.name) class Dog: def __init__(self,name,kind,food): self.name = name self.kind = kind self.food = food def eat(self): print('%s吃%s'%(self.name,self.food)) def drink(self): print('%s喝水'%self.name) def sleep(self): print('%s在睡觉'%self.name) def look_after_home(self): print('%s在看家'%self.name)
但是再往深入探讨,我们会发现其实编写的猫类和狗类代码中,很多都冗余代码。有什么方式能够把重复的代码提取出来吗?当然有,这时候就可以用到继承的思想,父类中的所有属性和方法都可以被子类使用。
#二。把冗余的代码提出来,抽象出一个宠物类,让猫类和狗类都继承这个宠物类。 class Pet: def __init__(self,name,kind,food): self.name = name self.kind = kind self.food = food def eat(self): print('%s吃%s'%(self.name,self.food)) def drink(self): print('%s喝水'%self.name) def sleep(self): print('%s在睡觉'%self.name) class Cat(Pet): def climb(self): print('%s在爬树'%self.name) class Dog(Pet): def kanjia(self): print('%s在看家'%self.name) tom = Cat('Tom','暹罗猫','猫粮') #子类使用名字(静态变量和方法),如果在子类中没有,就使用父类的 hei = Dog('小黑','二哈','狗粮') tom.climb() hei.kanjia()
2.继承与派生
子类也可以定义自己特有的属性或者重新这些属性(但并不会影响到父类),一旦定义了自己的属性且与父类同名时,这种属性称为派生属性。子类的对象调用这个属性时,会直接选择子类中的。
class Pet: def __init__(self,name,kind,food): print('IN PET') self.name = name self.kind = kind self.food = food def eat(self): print('%s吃%s'%(self.name,self.food)) def drink(self): print('%s喝水'%self.name) def sleep(self): print('%s在睡觉'%self.name) class Cat(Pet): def __init__(self,name,kind,food,eye_color): print('IN CAT') self.eye_color = eye_color #派生属性 # Pet.__init__(name,kind,food) super().__init__(name,kind,food) #super方法,可以省去self def climb(self): print('%s在爬树'%self.name) class Dog(Pet): def kanjia(self): print('%s在看家'%self.name) tom = Cat('阿猫','橘猫','猫粮','绿色') print(tom.eye_color)
# 派生经典考题: class Foo: def __init__(self): self.func() def func(self): print('in tho foo') class Son(Foo): def func(self): print('in the son') s1 = Son() >>> in the son ################ 当self去调用某个方法的时候,不要看self在哪里,而是要明确self代表的到底是谁。
派生总结:
1.当子类中方法要被调用的时候,子类的对象会直接选择子类中的方法。
2.父类的方法不会被自动执行。
3.如果我们既要执行子类的方法又要执行父类中的方法,那么需要在子类的方法中调用父类的方法,有以下两种方法:
(1)父类名.方法名(self,参数...)
(2)super().方法名(参数...) #关于super的查找顺序,请继续往下看。
3.抽象类
抽象类在python中使用的不是特别多,但是在以后的工作中,公司有可能会使用抽象类来制定开发的规则,在以后看源码的过程中,也可能会碰到抽象类的概念。试想一个场景:在今后的多人开发,功能复杂,许多后期拓展功能的项目中,代码的规范变得尤其的重要,而此时,抽象类就能够起到一个很好的模板作用。
抽象类只是一个模板的规范,基本不会实现什么功能,所以抽象类是不可以被实例化的。
#定义抽象类,需要引入一个模块。 from abc import ABCMeta,abstractmethod class Payment(metaclass=ABCMeta): #抽象类必须继承一个叫ABCMeta的类 @abstractmethod #抽象类规定的方法,用abstractmethod装饰器装饰,规定了 def pay(self):pass 后面凡是继承该抽象类的子类都必须有一个同名的方法。 class Alipay(Payment): def pay(self,money): print('使用支付宝支付了%s元'%money)
4.多继承
在python中允许一个子类是可以继承多个父类的。
class ParentClass1: #定义父类1 pass class ParentClass2: #定义父类2 pass class ChildClass(ParentClass1): #定义子类(继承父类1) 单继承 pass class ChildClass2(ParentClass1,ParentClass2): #多继承 pass
“__base__”:显示子类的一个父类。
“__bases__”:显示子类的所有父类。
5.新式类与经典类
python3.x所有的类都是新式类,所有的新式类都有一个默认继承的父类,叫做object类。
python2.7中经典类和新式类并存。
class Student:Pass #经典类
class Student(object):pass #新式类
总结:
(1)继承了object的类都是新式类。
(2)在py3中所有的类都是新式类。
(3)在py2中既有新式类也有经典类。
一.多继承的顺序/新式类和经典类之间的区别?
(1)钻石继承:广度优先。
新式类的所有多继承关系寻找方法的顺序--->遵循广度优先。
mro()--->显示多继承的继承顺序。
super()--->super方法不是单纯的寻找父类,在多继承中,是遵循mro循序的。
class A: def func(self): print('A') class B(A): def func(self): super().func() print('B') class C(A): def func(self): super().func() print('C') class D(B,C): def func(self): super().func() print('D') d = D() d.func() print(D.mro()) #新式类遵循广度优先。 >>> A C B D [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
总结:
新式类在多继承时,遵循广度优先。
经典类在多继承时,遵循深度优先。