一、继承概述
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”,继承的过程,就是从一般到特殊的过程。在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
继承概念的实现方式主要有2类:实现继承、接口继承。
实现继承是指使用基类的属性和方法而无需额外编码的能力。
接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力(子类重构爹类方法)。
在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。
二、实现继承
简单的继承
class Person:
def walk(self):
print("person is walking")
class Man(Person):
def talk(self):
print("man is talking")
p1 = Man()
print(Man.__dict__)
p1.walk()
p1.talk()
####结果
#{'__doc__': None, 'talk': <function man.talk at 0x10330dae8>, '__module__': '__main__'}
#person is walking
#man is talking
可以看出Man类继承了Person类的方法,但是在Man类的属性字典中除了自己定义的talk方法并没有walk方法,因此只有子类中没有找到类似的方法时才会去父类中寻找
方法重写
class Person:
def walk(self):
print("person is walking")
class Man(Person):
def walk(self):
print("man is walking")
def talk(self):
print("man is talking")
p1 = Man()
x1 = Person()
p1.walk()
x1.walk()
####结果
#{'__doc__': None, 'talk': <function man.talk at 0x10330dae8>, '__module__': '__main__'}
#man is walking
#person is walking
子类可以对父类的方法进行重写,而不会覆盖父类的方法
使用父类的方法
方法一
父类名.方法名
class Car:
def __init__(self,size,type,price):
self.size = size
self.type = type
self.price = price
class Battery(Car):
def __init__(self,size,type,price,battery):
Car.__init__(self,size,type,price)
self.battery = battery
def show_info(self):
print("%s的电池电量为%s"%(self.type,self.battery))
mycar = Battery('small','E1','100w','8000A/h')
mycar.show_info()
#E1的电池电量为8000A/h
使用此类调用父类的方法有缺陷,如果一个父类被若干个子类继承,父类一修改类名,则会导致代码有多处都需要修改,因此介绍方法二
方法二
super().方法名
class Car:
def __init__(self,size,type,price):
self.size = size
self.type = type
self.price = price
class Battery(Car):
def __init__(self,size,type,price,battery):
super().__init__(size,type,price)
#使用此种调用方法,也不用传入self参数
self.battery = battery
def show_info(self):
print("%s的电池电量为%s"%(self.type,self.battery))
mycar = Battery('small','E1','100w','8000A/h')
mycar.show_info()
#E1的电池电量为8000A/h
三、接口继承
声明某个子类兼容于某个父类,定义一个接口类,子类继承接口类,并且实现接口中定义的方法。
在实践中,接口继承比较常见并且实用,因为实现继承虽然减少了代码复用,但是这样子类和父类的耦合度较高。
而接口继承规定了一个兼容接口(接口就是一种方法),使得外部调用这可以使用统一的接口调用,这种方法叫做归一化设计。
linux中的一切皆文件,是此编程思想的最典型的例子。
import abc
class All_file(metaclass=abc.ABCMeta): # All_file为一个接口类
@abc.abstractmethod #此装饰器是为了子类必须对此接口方法进行重写,否则无法进行实例化
def read(self): # read为接口方法,没有任何实际方法
pass
@abc.abstractmethod
def wirte(self): # 同样也是接口方法
pass
class Disk(All_file):
def read(self):
print("Disk is reading")
def wirte(self):
print("Disk is wirteing")
class Mem(All_file):
def read(self):
print("Mem is reading")
def wirte(self):
print("Mem is wirteing")
四、继承顺序
若一个类继承了多个类,那么如果类在自己的类中找不到方法时,往父类中寻找方法的顺序是不同的。其中新式类和经典类的寻找顺序也不同
新式类指的是继承了object类的子类,而经典类在定义时没有继承任何类。在python3中定义的类都为新式类
#新式类的定义
class Nc(obejct):
pass
#经典类的定义
class Oc:
pass
新式类的继承顺序
class A:
def test(self):
print("这是A的方法")
class B(A):
def test(self):
print("这是B的方法")
class C(A):
def test(self):
print("这是C的方法")
class D(B):
def test(self):
print("这是D的方法")
class E(C):
def test(self):
print("这是E的方法")
class F(D,E):
def test(self):
print("这是F的方法")
f1 = F()
print(F.__mro__) #这是新式类包含的一个内置属性,保存了继承顺序的元祖
#(<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
从例子可见新式类的继承顺序是,F-D-B-E-C-A,顶级的父类是最后被寻找的,另一种被广泛的说法说这叫做广度寻找
经典类的继承顺序
经典类的继承顺序和新式类不太一样 ,继承顺序为F-D-B-A-E-C,这一般被叫做深度寻找。先从一个分支直接寻找至顶级父类,若还没有得到结果,则寻找分支。