组合

软件重用的重要方式除了继承之外还有另外一种方式,即:组合

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合

classschool:def __init__(self,name,addr):
self.name=name
self.addr=addrdeftype(self):print("%s 是 公立学校"%self.name)classstudent:'学生的信息'empcount=0def __init__(self,name,sex,age,school):
self.name=name
self.sex=sex
self.age=age
self.school=schooldeftype(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__)
(, )
(,)
继承顺序之MRO线性顺序
经典类:没有显示地继承object类的类,以及该类的子类,都是经典类==》深度查找
新式类:显示地继承object类的类,以及该类的子类,都是新式类==》广度查找

经典类顺序(深度):F--D--B--A 后F --E--C--A
新式类顺序(广度): F--D--B (不找基类)后 F--E--C--A
新式类可以直接通过 类名.__mro__的方式取类的MRO,也可以通过类目.mro()的形式
classA:pass
classB:pass
classC(A,B):pass
print(C.__mro__)print(C.mro())
”“”
执行结果
(, , , )
[, , , ]
“”“
在python3中都是同样的新式类,即使没有显示的继承object,也会默认继承该类
print(Parent1.__bases__) #输出 (,)
print(Parent2.__bases__) #输出 (,)
实例
classPeople:'''定义一个人类,
姓名、性别、年龄'''
def __init__(self,name,sex,age):
self.name=name
self.sex=sex
self.age=ageclassStudent(People):'''定义一个学生类'''
defstudying(self):print('%s is studying'%self.name)classTeacher(People):'''定义一个老师类'''
defteach(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__中找,如果没有则去子类中找,然后再去父类中找
继承-子类和父类的数据属性重名的问题
classPeople:'''定义一个人类,
姓名、性别、年龄'''school= '福大'
def __init__(self,name,sex,age):
self.name=name
self.sex=sex
self.age=ageclassStudent(People):'''定义一个学生类'''
defstudying(self):print('%s is studying'%self.name)classTeacher(People):'''定义一个老师类'''school= '北大' #子类自定义的属性和父类People的属性重名
defteach(self):print('%s is teaching'%self.name)
teacher1= Teacher('susu','未知',50)print(teacher1.school) #输出子类的 school为北大
print(People.school) #输出父类的 school 为福大 ;并不会被子类覆盖
派生与方法重用
父类中没有的属性 子类中出现 叫派生属性
父类中没有的方法 子类中出现 叫派生方法
父类和子类都有 调用子类 想调用父类的药指出父类名称调用
每个老师还有职称这个属性,则就需要在Teacher类中定义该类自己的__init__
classPeople:
school= '福大'
def __init__(self,name,sex,age):
self.name=name
self.sex=sex
self.age=ageclassStudent(People):defstudying(self):print('%s is studying'%self.name)classTeacher(People):def __init__(self,name,sex,age,titles):
self.name=name #重复代码
self.sex=sex #重复代码
self.age=age #重复代码
self.titles=titlesdefteach(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、直接调用某一个类的函数
classTeacher(People):def __init__(self, name, sex, age, titles):
People.__init__(self,name,sex,age) #调用的是函数,因而需要传入self
self.titles =titlesdefteach(self):print('%s is teaching'%self.name)
2、super()
调用super()会得到一个特殊的对象,该对象专门用来引用父类的属性,且严格按照MRO规定的顺序向后查找
classTeacher(People):def __init__(self, name, sex, age, titles):
super().__init__(name,sex,age) #调用的是绑定方法,自动传入self
self.titles =titlesdefteach(self):print('%s is teaching'%self.name)
组合和继承区别
1、当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好2、当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好
继承的作用
(1)减少重复代码,子类可扩展或者可以修改【但是往往在实践中,该含义意义并不很大,甚至常常是有害的,因为它使得子类与基类出现强耦合。】
(2)声明某个子类兼容于某基类,定义一个接口类,子类继承接口类,并且在子类实现接口中定义的方法————————————接口继承
【目的:规范子类,子类实现接口类的中定义的方法,并且不必要实例化】
【接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,一视同仁的处理实现了特定接口的所有对象”一这在程序设计上, 叫做归一化。】
抽象类
抽象类的本质上也是类,但是抽象类只能够被继承,不能进行实例化,也就是说可以当父类,但是不能生成对象。
当子类继承抽象类的时候,如果抽象类定义了抽象方法,那么子类必须要定义同名的方法,即父类限制:
1、子类必须要有父类的方法2、子类实现的方法必须跟父类的方法的名字一样
importabcclass Animal(metaclass=abc.ABCMeta):
@abc.abstractmethoddefeat(self):pass@abc.abstractmethoddefaction(self):pass
classCat(Animal):defeat(self,name):print('%s 在吃鱼' %name)
cat1=Cat()#会报错TypeError: Can't instantiate abstract class Cat with abstract methods action#因为子类Cat 没有和父类的方法一样,缺失一个action方法
importabcclass Animal(metaclass=abc.ABCMeta):
@abc.abstractmethoddefeat(self):pass@abc.abstractmethoddefaction(self):pass
classCat(Animal):defeat(self,name):print('%s 在吃鱼' %name)defaction(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('小黑猫')
”“”

小黑猫 在吃鱼

小黑猫 在喵喵叫

“”“