python3基础-组合和继承
组合
软件重用的重要方式除了继承之外还有另外一种方式,即:组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
class school: def __init__(self,name,addr): self.name=name self.addr=addr def type(self): print("%s 是 公立学校"%self.name) class student: '学生的信息' empcount = 0 def __init__(self,name,sex,age,school): self.name= name self.sex = sex self.age = age self.school =school def type(self): self.school.type() # 通过对象去调用type函数 print('哈哈哈哈哈') stu1 =student('susu','未知',18,school('北大','北京')) print(stu1.__dict__) print(stu1.name) print(stu1.school.name) print(stu1.school.addr) stu1.type()
继承
继承是一种创建新类的方式
在python中,新建的类可以继承一个或多个父类,新建的类可称为子类或派生类,父类又可以称为基类或超类
class Parent1: #定义父类 基类 超类 pass class Parent2: #定义父类 基类 超类 pass class Subclass1(Parent1):#单继承 子类 派生类 pass class Subclass2(Parent1,Parentc2):#多继承 子类 派生类 pass #类的内置属性__bases__可以查看类继承的所有父类 print(Subclass2.__bases__) print(Subclass1.__bases__)
(<class '__main__.Parent1'>, <class '__main__.Parent2'>)
(<class '__main__.Parent1'>,)
继承顺序之MRO线性顺序
经典类:没有显示地继承object类的类,以及该类的子类,都是经典类==》深度查找
新式类:显示地继承object类的类,以及该类的子类,都是新式类==》广度查找
经典类顺序(深度):F--D--B--A 后F --E--C--A
新式类顺序(广度): F--D--B (不找基类)后 F--E--C--A
新式类可以直接通过 类名.__mro__的方式取类的MRO,也可以通过类目.mro()的形式
class A: pass class B: pass class C(A,B): pass print(C.__mro__) print(C.mro()) ”“” 执行结果 (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>) [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>] “”“
在python3中都是同样的新式类,即使没有显示的继承object,也会默认继承该类
print(Parent1.__bases__) #输出 (<class 'object'>,) print(Parent2.__bases__) #输出 (<class 'object'>,)
实例
class People: ''' 定义一个人类, 姓名、性别、年龄 ''' def __init__(self,name,sex,age): self.name=name self.sex=sex self.age=age class Student(People): ''' 定义一个学生类 ''' def studying(self): print('%s is studying'%self.name) class Teacher(People): ''' 定义一个老师类 ''' def teach(self): print('%s is teaching'%self.name)
直接调用Teacher()
teacher1= Teacher() #未传入参数,则会报错TypeError: __init__() missing 3 required positional arguments: 'name', 'sex', and 'age'
Teacher类并没有定义__init__方法,但是会从父类中找到__init__则
teacher1=Teacher('苏苏','未知',20) print(teacher1.school,teacher1.name) teacher1.teach() #============= """ 执行结果如下 福大 苏苏 苏苏 is teaching """
总结:因为有了继承关系,对象在查找属性时,先从对象自己的__dict__中找,如果没有则去子类中找,然后再去父类中找
继承-子类和父类的数据属性重名的问题
class People: ''' 定义一个人类, 姓名、性别、年龄 ''' school = '福大' def __init__(self,name,sex,age): self.name=name self.sex=sex self.age=age class Student(People): ''' 定义一个学生类 ''' def studying(self): print('%s is studying'%self.name) class Teacher(People): ''' 定义一个老师类 ''' school = '北大' #子类自定义的属性和父类People的属性重名 def teach(self): print('%s is teaching'%self.name) teacher1 = Teacher('susu','未知',50) print(teacher1.school) #输出子类的 school为北大 print(People.school) #输出父类的 school 为福大 ;并不会被子类覆盖
派生与方法重用
父类中没有的属性 子类中出现 叫派生属性
父类中没有的方法 子类中出现 叫派生方法
父类和子类都有 调用子类 想调用父类的药指出父类名称调用
每个老师还有职称这个属性,则就需要在Teacher类中定义该类自己的__init__
class People: school = '福大' def __init__(self,name,sex,age): self.name=name self.sex=sex self.age=age class Student(People): def studying(self): print('%s is studying'%self.name) class Teacher(People): def __init__(self,name,sex,age,titles): self.name = name #重复代码 self.sex =sex #重复代码 self.age =age #重复代码 self.titles = titles def teach(self): print('%s is teaching'%self.name) teacher1 = Teacher('susu','女',29,'高级讲师') #只会找Teacher类中的__init__,并不会自动调用父类的 print(teacher1.name,teacher1.sex,teacher1.age,teacher1.titles) #输出的是susu 女 29 高级讲师
若想在子类派生的方法内重用父类的功能
1、直接调用某一个类的函数
class Teacher(People): def __init__(self, name, sex, age, titles): People.__init__(self,name,sex,age) #调用的是函数,因而需要传入self self.titles = titles def teach(self): print('%s is teaching'%self.name)
2、super()
调用super()会得到一个特殊的对象,该对象专门用来引用父类的属性,且严格按照MRO规定的顺序向后查找
class Teacher(People): def __init__(self, name, sex, age, titles): super().__init__(name,sex,age) #调用的是绑定方法,自动传入self self.titles = titles def teach(self): print('%s is teaching'%self.name)
组合和继承区别
1、当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
2、当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好
继承的作用
(1)减少重复代码,子类可扩展或者可以修改【但是往往在实践中,该含义意义并不很大,甚至常常是有害的,因为它使得子类与基类出现强耦合。】 (2)声明某个子类兼容于某基类,定义一个接口类,子类继承接口类,并且在子类实现接口中定义的方法————————————接口继承 【目的:规范子类,子类实现接口类的中定义的方法,并且不必要实例化】 【接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,一视同仁的处理实现了特定接口的所有对象”一这在程序设计上, 叫做归一化。】
抽象类
抽象类的本质上也是类,但是抽象类只能够被继承,不能进行实例化,也就是说可以当父类,但是不能生成对象。
当子类继承抽象类的时候,如果抽象类定义了抽象方法,那么子类必须要定义同名的方法,即父类限制:
1、子类必须要有父类的方法
2、子类实现的方法必须跟父类的方法的名字一样
import abc class Animal(metaclass=abc.ABCMeta): @abc.abstractmethod def eat(self): pass @abc.abstractmethod def action(self): pass class Cat(Animal): def eat(self,name): print('%s 在吃鱼' %name) cat1 =Cat() #会报错TypeError: Can't instantiate abstract class Cat with abstract methods action #因为子类Cat 没有和父类的方法一样,缺失一个action方法
import abc class Animal(metaclass=abc.ABCMeta): @abc.abstractmethod def eat(self): pass @abc.abstractmethod def action(self): pass class Cat(Animal): def eat(self,name): print('%s 在吃鱼' %name) def action(self,name): print('%s 在喵喵叫' %name) #ani = Animal() #不能进行实例化,会报错TypeError: Can't instantiate abstract class Animal with abstract methods action, eat cat1 =Cat() cat1.eat('小黑猫') cat1.action('小黑猫') ”“” 小黑猫 在吃鱼 小黑猫 在喵喵叫 “”“