我们都知道Python面向对象编程有三大特征,继承,封装和多态,下面几篇问题,我们会分别讲述着几大特征。
今天说的是继承,如果有编程基础的人对这个词应该不会陌生,继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类,而子类会“”遗传”父类的属性(数据属性和函数属性),从而解决代码重用问题。
上面这段话就是继承的概念和使用继承所要达到的目的。
下面我们来看看具体代码案例,继续的大致写法就是
子类名(父类1,父类2,。。。)
Python是可以实现对继承的,有些编程语法是只能单继承。
class ParentClass:
name= '父类'
def __init__(self,size,color):
self.size = size
self.color = color
def fun(self):
print('我来自父类')
class SubClass(ParentClass):
name = '子类'
pass
#实例化对象
s1 = SubClass('30','red')
#{'size': '30', 'color': 'red'} 如果子类没有构造方法的话,子类属性会找到父类的构造方法,继承父类的数据属性
print(s1.__dict__)
#(<class 'object'>,) python3中统一都是新式类,所以如果不加继承的父类,默认的父类是object类
print(ParentClass.__bases__)
#(<class '__main__.ParentClass'>,) 子类的父类
print(SubClass.__bases__)
# "我来自父类" 子类对象调用父类的方法
s1.fun()
#子类对象调用数据属性,会先在子类中找,如果找不到会去父类的作用域里面找
#这里子类里面定义了name属性所以结果是: 子类
#如果子类里面没有定name,结果就是:父类
print(s1.name)
这段是继承大致的用法,子类可以继承父类的数据属性和函数属性。下面我们说说使用继承的好处及代码重用和重写,组合的用法
代码重用
我们现在有2个类猫和狗,它们都是动物,那如果我们要描述猫和狗这2个类的话,我们会存在大量的重复代码,如下
class Cat:
def cry(self):
print('喵喵')
def eat(self):
print('吃')
def run(self):
print('跑')
def jump(self):
print('跳')
class Dog:
def cry(self):
print('汪汪')
def eat(self):
print('吃')
def run(self):
print('跑')
def jump(self):
print('跳')
cat1 = Cat()
dog1 = Dog()
cat1.cry()
dog1.cry()
上面2个类也许除了cry方法,其他的方法都是一样的,都猫和狗同意的动作,那像上面那样写,就会出现大量的重复代码,下面我们用继承来改写
class animal:
def cry(self):
print('动物叫')
def eat(self):
print('吃')
def run(self):
print('跑')
def jump(self):
print('跳')
class Cat(animal):
def cry(self):
print('喵喵')
class Dog(animal):
def cry(self):
print('汪汪')
cat1 = Cat()
dog1 = Dog()
cat1.cry() #喵喵
dog1.cry() #汪汪
cat1.eat() #吃
dog1.eat() #吃
上面我们定义了一个animal的父类,把子类的相同部分放入父类,子类就可以用继承的方式来调用父类的函数属性或者说方法了,而不用再类里面再重复编码。
- 类的方法重写
其实上面的例子我们已经用到重写,就是父类定义了一个方法,但是子类和父类的方法实现不一样,要达到另外一个功能,比较上面
父类的cry方法时: ‘动物叫’
子类Cat的cry要实现:‘喵喵叫’
子类Dog的cry要实现:‘汪汪叫’
这种情况就要使用重写
class animal:
def cry(self):
print('动物叫')
def eat(self):
print('吃')
def run(self):
print('跑')
def jump(self):
print('跳')
class Cat(animal):
def cry(self):
print('喵喵')
class Dog(animal):
def cry(self):
print('汪汪')
cat1 = Cat()
dog1 = Dog()
cat1.cry() #喵喵 重写父类cry方法
dog1.cry() #汪汪 重写父类cry方法
- 子类的方法派生,就是子类可以定义自己的方法
class animal:
def cry(self):
print('动物叫')
def eat(self):
print('吃')
def run(self):
print('跑')
def jump(self):
print('跳')
class Cat(animal):
def cry(self):
print('喵喵')
def swoop(self): #定义子类的方法
print('飞扑')
cat1 = Cat()
cat1.swoop()
4.类的组合,组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合,当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好.
class School:
def __init__(self,name,addr):
self.name=name
self.addr=addr
def recruit(self):
print('%s xxx计算机学校正在招生' %self.name)
class Course:
def __init__(self,name,price,period,School):
self.name=name
self.price=price
self.period=period
self.school=School #实现Course和School的组合
s1=School('xxx计算机学校','北京')
s2=School('xxx计算机学校','南京')
s3=School('xxx计算机学校','上海')
msg='''
1 xxx计算机学校 北京校区
2 xxx计算机学校 南京校区
3 xxx计算机学校 上海校区
'''
while True:
print(msg)
menu={
'1':s1,
'2':s2,
'3':s3
}
choice=input('选择学校>>: ')
school_obj=menu[choice]
name=input('课程名>>: ')
price=input('课程费用>>: ')
period=input('课程周期>>: ')
new_course=Course(name,price,period,school_obj)
print('课程【%s】属于【%s】学校' %(new_course.name,new_course.school.name)) #实现Course和School的组合
5.Python实现接口
接口就是定义抽象函数,不做具体函数实现,起到规范子类的作用,让子类必须实现接口的抽象函数。接口提取了一群类共同的函数,可以把接口当做一个函数的集合。然后让子类去实现接口中的函数。这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。
归一化的好处在于:
归一化让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合
下面上代码
import abc #利用abc模块实现抽象类
class Interface(metaclass=abc.ABCMeta):
@abc.abstractclassmethod #抽象方法,不做具体实现
def test1(self):
pass
@abc.abstractclassmethod
def test2(self):
pass
class SubClass(Interface):
def test1(self):
print('实现抽象方法1')
def test2(self):
print('实现抽象方法2')
s1 = SubClass()
如果我们子类里面不实现具体的抽象函数,会报错
import abc #利用abc模块实现抽象类
class Interface(metaclass=abc.ABCMeta):
@abc.abstractclassmethod #抽象方法,不做具体实现
def test1(self):
pass
@abc.abstractclassmethod
def test2(self):
pass
class SubClass(Interface):
def test1(self):
print('实现抽象方法1')
#不实现test2方法
s1 = SubClass()
报错信息:
Traceback (most recent call last):
File "C:/Users/aryin/Desktop/mysite2/继承.py", line 16, in <module>
s1 = SubClass()
TypeError: Can't instantiate abstract class SubClass with abstract methods test2
6.类继承的顺序,根据类的MRO属性
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')
# (<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
print(F.__mro__)
类继承是是按照MRO属性里面的顺序去调用父类的属性
7.子类调用父类的方法,是通过super函数来实现的,在复杂的类继承关系中,super()的取值是按照上面的MRO里面的顺序来定的。
class animal:
def __init__(self,name,type,size,color):
self.name = name
self.type = type
self.size = size
self.color = color
def cry(self):
print('动物叫')
def eat(self):
print('吃')
class Cat(animal):
def __init__(self,name,type,size,color,age):
super().__init__(name,type,size,color) #调用父类的构造函数,这是super最常用的地方
self.age = age
def cry(self):
super().cry() #子类重新了父类的方法,但是同时又要实现父类中cry方法
print('喵喵')
cat1 = Cat('毛球','波斯猫',10,'白色','5岁')
# 动物叫
# 喵喵
cat1.cry()