继承介绍、经典类和新式类、继承和抽象、封装和派生、组合

news/2024/10/1 23:42:41

【一】继承介绍

  • 继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。

  • 子类会“遗传”父类的属性,从而解决代码重用问题(去掉冗余的代码)

  • 继承:

    • 单继承:继承一个父类的子类
    • 多继承:继承多个父类的子类
class Student(School):
# 继承的类叫父类 School
# 新建的类叫子类 Student

【1】单继承和多继承

# 定义父类
class ParentClass1:pass# 定义父类
class ParentClass2:pass# 单继承,基类是ParentClass1,派生类是SubClass
class SubClass1(ParentClass1):pass# python支持多继承,用逗号分隔开多个继承的类
class SubClass2(ParentClass1, ParentClass2):pass
class School(object):school = '清华'class Person(object):height = 180weight = 50# 单继承
class Student(Person):def __init__(self, name):self.name = namedef tell_me(self):print(f"我是 {self.name} 我身高 {self.height} 体重 {self.weight}")stu1 = Student("chosen")
stu1.tell_me() # 我是 chosen 我身高 180 体重 50# 多继承
class Teacher(School, Person):def __init__(self, name):self.name = namedef tell_me(self):print(f"我是 {self.name} 我身高 {self.height} 体重 {self.weight} 学校在{self.school}")teacher = Teacher("max")
teacher.tell_me() # 我是 max 我身高 180 体重 50 学校在清华

【2】查看继承

# 查看继承的父类
print(Student.__base__)  # <class '__main__.Person'>
print(Teacher.__base__)  # <class '__main__.School'># 查看当前继承的父类们
print(Student.__bases__)  # (<class '__main__.Person'>,)
print(Teacher.__bases__)  # (<class '__main__.School'>, <class '__main__.Person'>)

【二】经典类和新式类

  • 只有在python2中才分新式类和经典类,python3中统一都是新式类
  • 在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类

【1】什么是经典类

  • 在 py2 中没有显示继承 object 的类或者是该类的子类都是经典类

【2】什么是新式类

  • 在 py2 中显示继承 object 的类或者是该类的子类都是新式类
  • 在 py3 之后所有的类默认都是新式类,不写默认继承 object

【三】继承和抽象(先抽象再继承)

  • 继承描述的是子类与父类之间的关系,是一种什么是什么的关系。
  • 抽象即抽取类似或者说比较像的部分

【1】抽象

  • 抽象分成两个层次
    • 将奥巴马和梅西这俩对象比较像的部分抽取成类;
    • 将人,猪,狗这三个类比较像的部分抽取成父类。
  • 抽象最主要的作用是划分类别

【2】继承

  • 继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
  • 抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类

【3】继承和抽象

  • 继承是由少变多
  • 抽象是由多变少

【4】在python中实现继承

[1]继承总类

  • 猫可以:喵喵叫、吃、喝、拉、撒
  • 狗可以:汪汪叫、吃、喝、拉、撒
# 有一只猫
class Cat(object):def speak(self):print(f"猫可以喵喵叫")def eat(self):print("猫可以吃饭")def drink(self):print("猫可以喝水")# 有一只狗
class Dog(object):def speak(self):print(f"狗可以旺旺叫")def eat(self):print("狗可以吃饭")def drink(self):print("狗可以喝水")

[2]代码实现

class Animal(object):def speak(self):print(f"{self.name}可以叫")def eat(self):print(f"{self.name}可以吃饭")def drink(self):print(f"{self.name}可以喝水")class Dog(Animal):def __init__(self, name):self.name = '狗' + nameclass Cat(Animal):def __init__(self, name):self.name = '猫' + namecat_one = Cat(name='小花')
cat_one.speak() # 猫小花可以叫
dog_one = Dog(name='旺财')
dog_one.speak() # 狗旺财可以叫

【四】封装后继承的属性查找顺序

【1】封装之前的属性查找顺序

  • 有了继承关系,对象在查找属性时
    • 先从对象自己的__dict__中找
    • 如果没有则去子类中找
    • 然后再去父类中找……
class Foo:def f1(self):print('Foo.f1')# 【3】发现父类中找到了 f2 方法def f2(self):print('Foo.f2')# 【4】# 但是这里犯了难,这个 self 到底是 Foo 还是 Bar ?# 我们要时刻记得,源头是谁,这个self就是谁# 我们是从 对象 b 来的,而对象 b 又是 Bar 的对象# 我们从 Bar 找到了 Foo 类里面,所以我们的源头就是 Bar# 那这个 self 就是 Bar , 而 Bar 类里面有 f1 方法# 所以我们就会回到 Bar 类里面self.f1()class Bar(Foo):# 【2】来到 Bar 类里面寻找 f2 方法,但是发现自己没有# 于是向上查找,去父类 Foo 中查找def f1(self):# 【5】 找到了 Bar 中的方法print('Bar.f1')# 实例化类得到对象
b = Bar()
# 【1】对象调用方法
b.f2()
# 【6】所以打印的顺序是 Foo 类中的 f2 , Bar 类中的 f1
# Foo.f2
# Bar.f1
  • b.f2()会在父类Foo中找到f2
    • 先打印Foo.f2
    • 然后执行到self.f1(),即b.f1()
  • 仍会按照:对象本身->类Bar->父类Foo的顺序依次找下去
    • 在类Bar中找到f1
    • 因而打印结果为Foo.f1

【2】有封装的时候的继承

  • 父类如果不想让子类覆盖自己的方法,可以采用双下划线开头的方式将方法设置为私有的
class Foo:# 变形为_Foo__fadef __f1(self):print('Foo.f1')def f2(self):print('Foo.f2')# 变形为self._Foo__fa,因而只会调用自己所在的类中的方法self.__f1()class Bar(Foo):# 变形为_Bar__f1def __f1(self):print('Bar.f1')b = Bar()
# 在父类中找到f2方法,进而调用b._Foo__f1()方法,同样是在父类中找到该方法
b.f2()
# Foo.f2
# Foo.f1

【3】总结

  • 如果属性不封装的情况下,谁实例化得到的 self 就去谁里面找
  • 如果属性封装的情况下,谁实例化得到的 self 无效,只能在当前所在的类的名称空间里面找

【五】派生

  • 派生是指子类继承父类,派生出自己的属性与方法,并且重用父类的属性与方法

【1】子类继承父类的属性

# 子类继承父类的属性
class People:school = '清华大学'def __init__(self,name,sex,age):self.name = nameself.sex = sexself.age = ageclass Teacher(People):# 派生 : 派生出自己新的属性,在进行属性查找时,子类中的属性名会优先于父类被查找def __init__(self,name,sex,age,title):self.name = nameself.sex = sexself.age = ageself.title = titledef teach(self):print('%s is teaching' % self.name)# 只会找自己类中的__init__,并不会自动调用父类的
obj = Teacher('chosen', 'male', 20, '高级讲师')print(obj.name, obj.sex, obj.age, obj.title)
# chosen male 20 高级讲师

【2】继承方式一

  • 指名道姓的调用某一个类的函数
class People:school = '清华大学'def __init__(self, name, sex, age):self.name = nameself.sex = sexself.age = ageclass Teacher(People):# 派生 : 派生出自己新的属性,在进行属性查找时,子类中的属性名会优先于父类被查找def __init__(self, name, sex, age, title):# 直接调用 父类 中 的 __init__ 方法# 调用的是函数,因而需要传入selfPeople.__init__(self, name, age, sex)self.title = titledef teach(self):print('%s is teaching' % self.name)# 只会找自己类中的__init__,并不会自动调用父类的
obj = Teacher('chosen', 'male', 20, '高级讲师')print(obj.name, obj.sex, obj.age, obj.title)
# # chosen male 20 高级讲师

【3】继承方式二

  • 调用super()会得到一个特殊的对象
  • 该对象专门用来引用父类的属性
  • 且严格按照MRO规定的顺序向后查找
class People:school = '清华大学'def __init__(self, name, sex, age):self.name = nameself.sex = sexself.age = ageclass Teacher(People):# 派生 : 派生出自己新的属性,在进行属性查找时,子类中的属性名会优先于父类被查找def __init__(self, name, sex, age, title):# 直接调用 父类 中 的 __init__ 方法# 调用的是绑定方法,因此会自动传入self,但是需要传入相应的参数super().__init__(name, sex, age)self.title = titledef teach(self):print('%s is teaching' % self.name)# 只会找自己类中的__init__,并不会自动调用父类的
obj = Teacher('chosen', 'male', 18, '高级讲师')print(obj.name, obj.sex, obj.age, obj.title)
# chosen male 18 高级讲师

【六】继承实现的原理

【1】非菱形结构继承的顺序

  • 在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如A(B,C,D)
  • 如果继承关系为非菱形结构,则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性

【2】菱形结构继承顺序

  • 如果继承关系为菱形结构,那么属性的查找方式有两种,分别是:深度优先和广度优先(当前 py3 版本实行广度优先)

[1]深度优先

  • 当类是经典类时,多继承情况下,在查找属性不存在时,会按照深度优先的方式查找下去

[2]广度优先

  • 当类是新式类时,多继承情况下,在查找属性不存在时,会按照广度优先的方式查找下去

【3】代码

class A(object):def test(self):print('from A')class B(A):def test(self):print('from B')class C(A):def test(self):print('from C')class D(B):def test(self):print('from D')class E(C):def test(self):print('from E')class F(D, E):# def test(self):#     print('from F')passf1 = F()
f1.test()
# 只有新式才有这个属性可以查看线性列表,经典类没有这个属性
print(F.__mro__)# 新式类继承顺序:F->D->B->E->C->A
# 经典类继承顺序:F->D->B->A->E->C
# python3中统一都是新式类
# pyhon2中才分新式类与经典类
  • 为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
  • 我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
    • 子类会先于父类被检查
    • 多个父类会根据它们在列表中的顺序被检查
    • 如果对下一个类存在两个合法的选择,选择第一个父类

【七】组合

  • 在一个类中以另一个类的对象作为数据属性,称为类的组合
class Course:def __init__(self, name, period, price):self.name = nameself.period = periodself.price = pricedef tell_info(self):print(f'当前课程名字 {self.name} 当前课程周期 {self.period} 当前课程价格 {self.price}')class Date:def __init__(self, year, mon, day):self.year = yearself.mon = monself.day = daydef tell_birth(self):print(f'当前生日 {self.year} 年 {self.mon} 月 {self.day} 日')class People:school = '清华大学'def __init__(self, name, sex, age):self.name = nameself.sex = sexself.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)# 老师有课程,可以在实例化后,往该列表中添加Course类的对象self.courses = []def teach(self):print(f'当前老师正在授课 {self.name}')python = Course('python', '3mons', 3000.0)
linux = Course('linux', '5mons', 5000.0)
teacher1 = Teacher('dream', 'male', 18, '金牌讲师', 1987, 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()# 当前生日 1987 年 3 月 23 日
# 当前课程名字 python 当前课程周期 3mons 当前课程价格 3000.0
# 当前课程名字 linux 当前课程周期 5mons 当前课程价格 5000.0

【1】组合和继承的区别

  • 组合与继承都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同,

【2】继承的方式

  • 通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。
  • 当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人

【3】组合的方式

  • 用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python和linux课程,教授有学生s1、s2、s3...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/29508.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

CH57x/CH58X/CH59X/CH32F/V208OTA使用说明

目前提供了两种OTA升级方式, 方式一:带库升级;每次升级可以带着库一起进行升级(带库升级适用于flash较大的芯片) 方式二:固定库升级;升级时库不会随着升级而升级(适用于flash不够用时) 方式一: 升级时需要同时烧录这三个固件:(可以使用isp工具同时烧录也可以使用合并…

loons2024年05月09日20:04:57

1 1 1 11 1 1 11 1 1 11 1 1 11 1 1 11 1 1 11 1 1 1

邮件的发送

邮件发送和接收的协议 SMTP协议 (Simple Mail Transfer Protocol)属于TCP/IP协议族。 控制信件的中转方式,帮助每台计算机在发送或中转信件时找到下一个目的地。 SMTP服务器是遵循SMTP协议的发送邮件服务器。POP3协议 (Post Office Protocol - Version 3)属于TCP/IP协议族。…

P3842 [TJOI2007] 线段

洛谷-题目链接 [TJOI2007] 线段提示 我们选择的路线是(1, 1) (1, 6)(2, 6) (2, 3)(3, 3) (3, 1)(4, 1) (4, 2)(5, 2) (5, 6)(6, 6) (6, 4) (6, 6)不难计算得到,路程的总长度是 24。代码代码 #include <bits/stdc++.h> using namespace std;const int N = 2e4+5;int n; /…

ssh、PAM模块

.ssh/known_hosts 存储ssh指纹 sshd 服务器端 /etc/ssh/sshd_config 服务器端的配置文件 man 5 sshd_config 服务器端的配置文件帮助 echo root:1111|chapasswd 修改密码 openssl rand -base 64 9 随机取9位密码(随机数经过base编码取9位) ssh常用参数: Port 22 #生产建…

敏捷冲刺day2--数字工匠队

这个作业属于哪个课程 软件工程这个作业的要求是什么 项目冲刺这个作业的目标 冲刺日志2站立式会议照片工作困难 有部分知识不知道,要额外学习 昨日完成工作 用户登录前面前端初版 今日计划工作 登录界面前后端处理 项目燃尽图每日总结 陈家谦:继续学习 陆靖:继续努力 代码签…

CloudXR:更高效便捷的XR应用交互方案

CloudXR是一种新颖而先进的技术,旨在将虚拟现实和增强现实体验从本地设备转移到云端,主要功能也包括了远程渲染、流媒体传输、低延迟、高带宽和高质量的音视频传输。云化XR可以将高保真度的虚拟现实或增强现实场景实时传输到终端设备上,用户只需通过互联网即可感受到身临其境…