首页 > 其他分享 >面向对象的三大特性之继承

面向对象的三大特性之继承

时间:2024-01-29 14:58:57浏览次数:14  
标签:__ 继承 子类 self 特性 面向对象 三大 print def

面向对象的三大特性之继承

一、什么是继承

  • 继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。
  • 子类会“”遗传”父类的属性,从而解决代码重用问题(去掉冗余的代码)
  • python中类的继承分为:单继承和多继承

二、单继承与多继承

class Parent1(object):
    ...


class Parent2(object):
    ...


# 单继承,基类是Parent1,派生类是SubClass1
class SubClass1(Parent1):
    ...


# python支持多继承,用逗号分隔开多个继承的类
class SubClass2(Parent1, Parent2):
    ...

三、查看继承

# __base__只查看从左到右继承的第一个子类
print(SubClass2.__base__)  # (<class '__main__.Parent1'>,)
# __bases__则是查看所有继承的父类
print(SubClass2.__bases__)  # (<class '__main__.Parent1'>, <class '__main__.Parent2'>)

四、经典类与新式类

  • 只有在python2中才分新式类和经典类,python3中统一都是新式类
  • 在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
  • 在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
  • 在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类

提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。

class Parent1(object):
    ...


class Parent2(object):
    ...

    
print(Parent1.__bases__)  # (<class 'object'>,)
print(Parent2.__bases__)  # (<class 'object'>,)

五、继承与抽象

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

[1]抽象

  • ​ 抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)

[2]继承

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

六、继承与重用性

[1]继承可以减少代码的重复和冗余

案例:

  • 如果我们要建立几个有相同功能或者逻辑的类,如下:
# 猫会喵喵叫,会吃喝拉撒睡,而狗会汪汪叫,也会吃喝拉撒睡
#猫类:
class Cat(object):
    def eat(self):
        print("猫会吃东西")

    def drink(self):
        print("猫会喝水")

    def excrete(self):
        print("猫会用猫砂盆")

    def sleep(self):
        print("猫会睡觉")

    def meow(self):
        print("猫会喵喵叫")

#狗类:
class Dog(object):
    def eat(self):
        print("狗会吃东西")

    def drink(self):
        print("狗会喝水")

    def excrete(self):
        print("狗会用猫砂盆")

    def sleep(self):
        print("狗会睡觉")

    def bark(self):
        print('狗会汪汪叫')
  • 如果使用 继承 的思想,如下实现:
    • 动物:吃、喝、拉、撒
    • 猫:喵喵叫(猫继承动物的功能)
    • 狗:汪汪叫(狗继承动物的功能)
class Animal(object):
    def eat(self):
        print("会吃东西")

    def drink(self):
        print("会喝水")

    def excrete(self):
        print("会用猫砂盆")

    def sleep(self):
        print("会睡觉")


class Cat(Animal):
    def meow(self):
        print("猫会喵喵叫")


class Dog(Animal):
    def bark(self):
        print('狗会汪汪叫')


dog = Dog()
dog.eat()  # 会吃东西
dog.bark()  # 狗会汪汪叫

cat = Cat()
cat.drink()  # 会喝水
cat.meow()  # 猫会喵喵叫

[2]代码的重用

  • 在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时
  • 我们不可能从头开始写一个类B,这就用到了类的继承的概念。
  • 通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用

提示:用已经有的类建立一个新的类,这样就重用了已经有的软件中的一部分设置大部分,大大生了编程工作量,这就是常说的软件重用,不仅可以重用自己的类,也可以继承别人的,比如标准库,来定制新的数据类型,这样就是大大缩短了软件开发周期,对大型软件开发来说,意义重大.

七、属性查找顺序

[1]不隐藏属性

  • 有了继承关系,对象在查找属性时
    • 先从对象自己的__dict__中找
    • 如果没有则去子类中找
    • 再去上一级的父类里找
    • 直至找到属性或者找不到报错
class Parent(object):
    def index(self):
        print('这是父类中的index')

    def func(self):
        print('这是父类中的func')
        self.index()


class SubClass(Parent):
    def index(self):
        print('这是子类中的index')

#实例化对象obj
obj = SubClass()

#子类中没有func,去父类中找
obj.func()
'''
父类中的func
这是子类中的index 
'''
'''
ps:为何使用的是子类中的index?
当子类去父类中寻找func是带过去的self属性是子类示例化出来的对象所以先从子类中开始找index所以最后执行的是子类中的代码
'''

[2]隐藏属性

class Parent(object):
    def __index(self):
        print('这是父类中的index')

    def func(self):
        print('这是父类中的func')
        self.__index()


class SubClass(Parent):
    def __index(self):
        print('这是子类中的index')


obj = SubClass()

obj.func()
"""
这是父类中的func
这是父类中的index
"""
"""
因为将index改为了__index 那么在父类中执行self.__index 就等于执行 self._Parent__index,将这个方法私有了,所以就会执行得类中的代码
"""

八、继承实现的原理

[1]非菱形结构的继承顺序

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

[2]菱形结构的继承顺序

  • 如果继承关系为菱形结构,那么属性的查找方式有两种,分别是:深度优先和广度优先

(1)深度优先

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

(2)广度优先

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

(3)代码

  • 使用的是Python3.10版本所以类全都是新式类,依照广度优先原则
class X(object):
    def test(self):
        print('这是X')


class A(X):
    def test(self):
        print('这是A')


class B(A):
    def test(self):
        print('这是B')


class C(A):
    def test(self):
        print('这是C')


class D(C):
    def test(self):
        print('这是D')


class E(X):
    def test(self):
        print('这是E')


class F(B, D, E):
    ...


f = F()
f.test()
print(F.__mro__)
# F>B>D>C>A>E>X
# (<class '__main__.F'>, 
# <class '__main__.B'>, 
# <class '__main__.D'>, 
# <class '__main__.C'>, 
# <class '__main__.A'>, 
# <class '__main__.E'>, 
# <class '__main__.X'>, 
# <class 'object'>)

九、Mixins机制

[1]引入

  • 一个子类可以同时继承多个父类,这样的设计常被人诟病
    • 一来它有可能导致可恶的菱形问题
    • 二来在人的世界观里继承应该是个”is-a”关系。
  • 比如轿车类之所以可以继承交通工具类,是因为基于人的世界观
    • 我们可以说:轿车是一个(“is-a”)交通工具
    • 而在人的世界观里,一个物品不可能是多种不同的东西,

[2]多重继承问题

  • 因此多重继承在人的世界观里是说不通的
    • 它仅仅只是代码层面的逻辑。
    • 不过有没有这种情况,一个类的确是需要继承多个类呢?
  • 答案是有,我们还是拿交通工具来举例子:
    • 民航飞机、直升飞机、轿车都是一个(is-a)交通工具
      • 前两者都有一个功能是飞行fly,但是轿车没有
      • 所以如下所示我们把飞行功能放到交通工具这个父类中是不合理的

[3]Mixins机制详解

  • 简单来说Mixins机制指的是子类混合(mixin)不同类的功能

  • 就是将那些放入父类会影响其他子类功能实现的或者其他子类不应该拥有的,但如果提出来再创建一个独特的父类又违背了继承最初功能(减少代码的冗余)的特定功能使用特定的命名规范进行单独表示成功能类的行为。(个人理解)

  • 大白话说就是为了遵循继承的“is-a”原则,子类只能继承一个标识其归属含义的父类(儿子只能有一个父亲),而其他类都是功能类,子类只是使用其类中的功能,而不属于此类。

  • 将麻烦的功能提出来重新命名并单独成类

class Vehicle(object):
    def __init__(self, name):
        self.name = name

    # 定义方法‘可以载人’
    def carry(self):
        print(f'{self.name}可以载人')


# 定义功能类‘能飞’
class Flyable(object):
    def __init__(self, name):
        self.name = name

    # 定义方法‘能飞
    def fly(self):
        print(f'{self.name}能飞')


# 子类’飞机‘,继承了父类‘交通工具’和功能类‘能飞’
class Plane(Vehicle, Flyable):
    def __init__(self, name):
        super().__init__(name)


# 子类’汽车‘,继承了父类‘交通工具’
class Car(Vehicle):
    def __init__(self, name):
        Vehicle.__init__(self, name)


plane_1 = Plane('波音747')
plane_1.carry()
plane_1.fly()
car_1 = Car('五菱宏光')
car_1.carry()
  • 例如上面代码中的能飞的功能类,如果放入父类交通工具就会导致其他继承的子类:汽车子类就拥有了能飞的功能,不符合常理。于是将能飞这个功能单独拿出来,定义类一个功能类

标签:__,继承,子类,self,特性,面向对象,三大,print,def
From: https://www.cnblogs.com/taoyuanshi/p/17994491

相关文章

  • 面向对象之抽象类
    面向对象之抽象类一、接口与抽象类[1]什么是接口因为Python中没有具体的接口概念我们以Java中的接口来对接口进行解释:我们以IAnimal.java(动物类)为例Java的Interface接口的特征是一组功能的集合,而不是一个功能接口的功能用于交互,所有的功能都是public,即别的对象可操作......
  • 面向对象之元类
    面向对象之元类一、什么是元类产生已知类的类就叫做元类,typeclassPerson(object):...deffunc():...people=Person()#产生对象的是类print(type(people))#<class'__main__.Person'>print(type(func))#<class'function'>#产生类的就是元类p......
  • 面向对象之内置方法
    面向对象之内置方法​Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发__init__ :初始化类时触发__del__ :删除类时触发__new__ :构造类时触发__str__ :str函数或者print函数触发__......
  • 面向对象之派生和组合
    面向对象之派生和组合派生派生是指,子类继承父类,派生出自己的属性与方法,并且重用父类的属性与方法一、派生的方法子类是必须要派生出自己的数据属性不然就无数据属性可用子类无法使用父类中__init__定义的属性classPerson(object):def__init__(self,name,age......
  • 面向对象基础 成员变量、成员方法、构造方法、this关键字、静态字段、静态方法..
    成员变量与局部变量的区别:  1.作用域:成员变量作用于整个类,局部变量只作用于它所属的范围(函数、语句)  2.生命周期&位置:成员变量存储在堆内存中,是属于对象的,随着对象存在消失。局部变量存储在栈内存中,是属于他所属的范围的,使用完自动释放。  3.初始值:成员变量有默认初始......
  • day01-面向对象高级
    day01——面向对象高级各位同学,接下来的三天课程中,我们继续学习面向对象的相关课程。面向对象是写Java程序的核心套路,如何你不懂面向对象,那就相当于Java你白学了。所以在接下来的三天时间里,各位同学也需要克服重重困难好好学习。前面我们说过面向对象最核心的套路是:设计对象来处......
  • C# 面向对象编程进阶:构造函数详解与访问修饰符应用
    C#构造函数构造函数是一种特殊的方法,用于初始化对象。构造函数的优势在于,在创建类的对象时调用它。它可以用于为字段设置初始值:示例获取您自己的C#服务器创建一个构造函数://创建一个Car类classCar{publicstringmodel;//创建一个字段//为Car类创建一个......
  • C# 面向对象编程进阶:构造函数详解与访问修饰符应用
    C#构造函数构造函数是一种特殊的方法,用于初始化对象。构造函数的优势在于,在创建类的对象时调用它。它可以用于为字段设置初始值:示例获取您自己的C#服务器创建一个构造函数://创建一个Car类classCar{publicstringmodel;//创建一个字段//为Car类创建一......
  • 无涯教程-Scala - 特性(Traits)
    Traits封装了方法和字段定义,然后可以通过混合到类中来重用它们。与类继承不同,一个类可以混合任意数量的traits。traits定义看起来类似于类定义,只是它使用关键字trait。以下是trait的基本示例语法。Traits-语法traitEqual{defisEqual(x:Any):BooleandefisNot......
  • 修改vSphere ESXi中虚拟机的三大件CPUID,硬盘ID,MAC地址
    1、查看硬件信息 wmicdiskdrivegetserialnumber查看磁盘序列号wmicbiosgetserialnumber查询BIOS序列号wmicnicconfiggetmacaddress查询网卡MAC信息wmiccpugetprocessorid查询cpu的IDwmicbeseboardgetserialnumber查询主板序列号 2、修改CPUID......