首页 > 其他分享 >继承介绍、经典类和新式类、继承和抽象、封装和派生、组合

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

时间:2024-05-09 20:44:22浏览次数:12  
标签:__ 封装 name 派生 继承 self print 父类 def

【一】继承介绍

  • 继承是一种创建新类的方式,新建的类可以继承一个或多个父类(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 = 180
    weight = 50


# 单继承
class Student(Person):
    def __init__(self, name):
        self.name = name

    def 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 = name

    def 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 = '狗' + name


class Cat(Animal):
    def __init__(self, name):
        self.name = '猫' + name


cat_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__fa
    def __f1(self):
        print('Foo.f1')

    def f2(self):
        print('Foo.f2')
        # 变形为self._Foo__fa,因而只会调用自己所在的类中的方法
        self.__f1()


class Bar(Foo):
    # 变形为_Bar__f1
    def __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 = name
        self.sex = sex
        self.age = age

class Teacher(People):
    # 派生 : 派生出自己新的属性,在进行属性查找时,子类中的属性名会优先于父类被查找
    def __init__(self,name,sex,age,title):
        self.name = name
        self.sex = sex
        self.age = age
        self.title = title
    def 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 = name
        self.sex = sex
        self.age = age


class Teacher(People):
    # 派生 : 派生出自己新的属性,在进行属性查找时,子类中的属性名会优先于父类被查找
    def __init__(self, name, sex, age, title):
        # 直接调用 父类 中 的 __init__ 方法
        # 调用的是函数,因而需要传入self
        People.__init__(self, name, age, sex)
        self.title = title

    def 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 = name
        self.sex = sex
        self.age = age


class Teacher(People):
    # 派生 : 派生出自己新的属性,在进行属性查找时,子类中的属性名会优先于父类被查找
    def __init__(self, name, sex, age, title):
        # 直接调用 父类 中 的 __init__ 方法
        # 调用的是绑定方法,因此会自动传入self,但是需要传入相应的参数
        super().__init__(name, sex, age)
        self.title = title

    def 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')
    pass


f1 = 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 = name
        self.period = period
        self.price = price

    def tell_info(self):
        print(f'当前课程名字 {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(f'当前生日 {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)
        # 老师有课程,可以在实例化后,往该列表中添加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...

标签:__,封装,name,派生,继承,self,print,父类,def
From: https://www.cnblogs.com/chosen-yn/p/18183038

相关文章

  • React文本溢出组件封装以及高亮提示
    React文本溢出组件封装以及高亮提示Abbr组件:使用场景:当我们需要设置支持最大行数时进行省略展示当我们需要设置支持设置超过多少字符进行省略展示当我们需要设置支持关键字高亮展示(有点问题,当关键字被裁剪成...之后,就无法高亮)当我们需要支持忽略大小写高亮当我们需要支持......
  • 面向对象的三大特性、封装的介绍及属性、装饰器property、员工管理系统
    【一】面向对象的三大特性封装:封装指的就是把数据与功能都整合到一起继承多态【二】什么是封装封装是对具体对象的一种抽象意思就是将某部分功能和代码隐藏起来,在程序外边看不到,只能在程序内部使用【三】为什么要封装?封装数据的主要原因是:保护隐私(把不想别人......
  • 使用同事封装好的table(使用el-table),修改表头背景色怎么实现
    花了一下午时间基本功还是比较差参考博客1首先,要修改表头颜色,需要el-table的属性:header-cell-style可以这样写:header-cell-style="{background:'#409EFF',color:'#409EFF'}"而我有两个点需要考虑1)只有部分表头需要修改颜色2)同事封装的组件,对背景颜色使用了!important解决......
  • UIOTOS前端零代码应用 蓝图连线+嵌套封装 实现MQTT输入0、1切换门禁开和关
    目标通过连线+嵌套封装,实现MQTT数据推送前端控制门禁开关最终效果实现过程步骤1:新建一个页面,在工具函数input输入两个门禁的开关图标地址,再通过连线和解析实现输0、1切换图标。 步骤2:再插入图片按钮,通过连线和解析,把工具函数的输出传递给图片按钮组件的path属性。 步骤......
  • 【HarmonyOS Next】多线程网络请求简单封装
    importhttpfrom'@ohos.net.http';import{taskpool}from'@kit.ArkTS';exportclassRequest{staticasyncget(url:string,header?:Object,expectDataType:http.HttpDataType=http.HttpDataType.OBJECT):Promise<Object>{......
  • python 封装与解构
    封装封装是面向对象编程中的一个概念,指的是将数据(属性)和操作(方法)捆绑到一个单元中,通过这种方式实现数据隐藏和封装。在Python中,类是封装的主要机制,将数据和方法封装在类的内部,并通过对象来访问。通过封装,可以实现数据的保护和操作的封装,提高代码的模块化程度和安全性。classPla......
  • 类的继承和派生
    【一】继承介绍#面向对线三大特性:继承封装多态#继承#【一】什么是继承#继承就是一种创建新类的方式,新建的类可以继承一个或多个类的属性。#新的类如果有自己的属性,那就叫派生#【二】继承的优点#可以继承父类的所有属性和方法,实现代码的去重#【三】继承方式#......
  • 基于Lua实现类的继承
    --参考Lua官方给的代码http://lua-users.org/wiki/ClassesAndMethodsExample学习如何基于Lua脚本实现继承和面向对象的机制。下面是测试代码。--objecttest.luarequire("INC_Class")--===========================localcAnimal=SETclass("Animal")functioncAni......
  • kiCad新建符号库(系统自带封装库)
    软件版本:KiCad8.01打开软件,选择工程,并打开原理图:2点击符号编辑器:3新建符号搜索XL,在Regulator_Switching(稳压器开关)一栏中已有一部分XL系列的稳压芯片:在此基础上新建一个XL7056的降压型DC-DC转换器,右键,选择新建符号:输入新建的符号名:此时,在Regulator_Switching(稳......
  • c++继承两个类怎么实现
    在C++中,继承两个类可以通过多重继承来实现。多重继承允许一个派生类从多个基类继承属性和方法。以下是一个继承两个类的示例:#include<iostream>//第一个基类classBase1{public:voidmethod1(){std::cout<<"Base1method1"<<std::endl;}};//......