一、继承
1.1、何为继承?
继承是一种创建新类的方式,新建的类可称为子类或派生类,父类别称基类或超类!Python支持多继承,子类可继承一个或多个父类,同样的,子类会继承父类的属性!
1.1.1、多继承
优点:子类可以同时继承多个父类的属性遗传,最大限度地提高代码重复使用率!
缺点:违背了人的思维习惯,代码可读性变差,扩展性变差,可能会引发恶性的菱形问题!非要用 应该使用mixins
1.1.2、新式类与经典类
在Python3中,所有类都默认继承object类,就好像无论是欧洲人、亚洲人、美洲人都是人一样,这样的类我们称之为新式类!在Python2中,默认不继承object的类及其子类,我们称之为经典类!当然了,Python2你也可以指定继承object,这样就变新式类了!
两者有什么区别?请听我后面慢慢分解!
1.2、为何用继承?
据上所述,子类会继承父类的属性,也意味着减少类与类之间的代码冗余!
1.3、如何用继承?
1.3.1、查看继承关系
bases方法可以查看继承关系
class School: # 父类
...
class Guangzhou: # 父类
...
class Student(School): # 单继承
...
class Teacher(School, Guangzhou): # 多继承
...
print(Teacher.__bases__) # 查看继承关系
# 执行得到结果
(<class '__main__.School'>, <class '__main__.Guangzhou'>)
二、继承与抽象
将对象抽象化,提炼其相似之处即可分门归类;提炼类之间的相似之处即可得到父类!我画了一张图来阐述:
三、属性查找
3.2、单继承
对象查找属性时,先从自身找起,无则去类中找,再无则去父类中找,如此类推!
3.3、多继承
多继承情况下,查找前提还是跟单继承一样,但是多继承很有可能引发菱形问题(有兴趣自己网上搜索相关文章),比如说一个套一个,套的还是同一个类,那该用谁?因此Python给定义的每一个类计算出一个查找顺序表:MRO,查找属性时会依照这个表的顺序一直找下去!以上面例子为准:
print(Teacher.__mro__)
# 执行得到结果
(<class '__main__.Teacher'>, <class '__main__.School'>, <class '__main__.Guangzhou'>, <class 'object'>)
总的来说,发起属性查找,会先从自身找起,无则按照mro列表来找!
四、Mixins机制
Python独特的多继承很容易使人搞混,代码可读性大大降低!因此Python提供了一种规范:Mixins,当需要混合不同类的功能时,这些被此采用的类使用统一的命名规范,比如Mixins结尾,Mixins应该具备以下条件:
4.1、Mixins规范
1、以Mixins结尾的名称应该是代表着某种功能,比如说writeMixins,而不是某种物品或者说数据属性,如bookMixins!
2、功能应该是单一的,如果需要混用多个功能,那就分开写多个Mixins类!
3、不依赖于其它类,即不继承其它类!
4、子类即使没继承Mixins类,也能正常执行!
class Animal: # 父类
...
class Cattle(Animal):
...
class swimMixins: # 游泳功能
print('I can swim')
class Fish(swimMixins, Animal): # 父类是Animal,swimMixins仅作为一种功能加入
...
五、派生与重用
派生即在类的基础上衍生新属性,先上车再解释:
class School:
school = 'Python'
def __init__(self, name, age):
self.name = name
self.age = age
class Student(School):
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
class Teacher(School):
def __init__(self, name, age, gender, salary):
self.name = name
self.age = age
self.gender = gender
self.salary = salary
派生即在类的基础上衍生新属性,上面的例子很明显代码冗余了!但是student和teacher又比父类多出那么一点不一样,能不能较少重复的那几行代码?
5.1、代码重用
在派生的子类中如何重用父类的功能?
方式一:直接调用
直接调用某一个类下的函数,而不依赖于继承关系
class Student():
def __init__(self, name, age, homework):
School.__init__(self, name, age) # 不依赖继承,因此必须手动传入对象self
self.homework = homework
方式二:super()
调用super会得到一个特殊对象super,该对象专用于引用父类属性!此方法依赖于继承关系,且属性查找顺序严格依照mro!
class Teacher(School):
def __init__(self, name, age, salary):
super().__init__(name, age) # 依赖继承,因此不需要手动传入对象
self.salary = salary
5.2、组合
以下内容均转载自知乎egon老师第六节:组合,链接地址:https://zhuanlan.zhihu.com/p/109331525
在一个类中以另外一个类的对象作为数据属性,称为类的组合。组合与继承都是用来解决代码的重用性问题。不同的是:继承是一种“是”的关系,比如老师是人、学生是人,当类之间有很多相同的之处,应该使用继承;而组合则是一种“有”的关系,比如老师有生日,老师有多门课程,当类之间有显著不同,并且较小的类是较大的类所需要的组件时,应该使用组合,如下示例
class Course:
def __init__(self,name,period,price):
self.name=name
self.period=period
self.price=price
def tell_info(self):
print('<%s %s %s>' %(self.name,self.period,self.price))
class Date:
def __init__(self,year,mon,day):
self.year=year
self.mon=mon
self.day=day
def tell_birth(self):
print('<%s-%s-%s>' %(self.year,self.mon,self.day))
class People:
school='清华大学'
def __init__(self,name,sex,age):
self.name=name
self.sex=sex
self.age=age
#Teacher类基于继承来重用People的代码,基于组合来重用Date类和Course类的代码
class Teacher(People): #老师是人
def __init__(self,name,sex,age,title,year,mon,day):
super().__init__(name,age,sex)
self.birth=Date(year,mon,day) #老师有生日
self.courses=[] #老师有课程,可以在实例化后,往该列表中添加Course类的对象
def teach(self):
print('%s is teaching' %self.name)
python=Course('python','3mons',3000.0)
linux=Course('linux','5mons',5000.0)
teacher1=Teacher('lili','female',28,'博士生导师',1990,3,23)
# teacher1有两门课程
teacher1.courses.append(python)
teacher1.courses.append(linux)
# 重用Date类的功能
teacher1.birth.tell_birth()
# 重用Course类的功能
for obj in teacher1.courses:
obj.tell_info()
此时对象teacher1集对象独有的属性、Teacher类中的内容、Course类中的内容于一身(都可以访问到),是一个高度整合的产物