面向对象的三大特性:封装、继承、多态

面向对象编程简单理解就是:基于模板(类)去创建实体(对象),使用对象完成功能开发。

面向对象包含 3 大主要特性:封装、继承、多态。下面,我们将逐一介绍。

一、封装

封装:将现实世界事物的属性和行为封装到类中,描述为成员变量和成员方法,从而完成程序对现实世界事物的描述。

现实世界中事物的部分属性和行为是不能开放给用户使用的,例如在使用手机时,剩余电量,打电话,拍照等属性和行为对用户开放,而驱动信息,程序调度等属性和行为,对用户而言是隐藏的。

作为现实事物在程序中映射的类,提供了私有成员的形式来支持现实事物有不公开的属性和行为。

定义私有成员的方式:

  • 私有成员变量:变量名以 __ 开头( 2 个下划线)
  • 私有成员方法:方法名以 __ 开头( 2 个下划线)

注意:私有成员无法被类对象使用(私有变量无法赋值,私有方法无法被调用,强行调用会报错),但是可以被类中其它的成员使用,如下图所示:

代码示例:

#设计类
class Phone:
    IMEI=None      #序列号
    producer=None  #厂商

    __current_voltage=5 #当前电压

    def call_by_5G(self):
        if self.__current_voltage>=1: #私有成员变量被其他成员使用
            self.__keep_single_core() #私有成员方法被其他成员使用
            print('5G通话已开启')
        else:
            print('通话失败,电量不足')

    def __keep_single_core(self):
        print('让CPU以单核模式运行节省电量')
#创建对象
phone=Phone()
phone.call_by_5G()

输出:

让CPU以单核模式运行节省电量
5G通话已开启

二、继承

软件更新时,软件包里类的部分,每次更新是重新写这个类,还是在原有基础是增删改动呢?答案显而易见,每次更新都要重写这个类,效率太低了,程序员肯定是在原有基础上进行改动,这就涉及到了类的继承。

继承表示从父类那里继承(复制)来成员变量和成员方法(不含私有),被继承的称为父类,主动继承的称为子类,从父类中继承过来的成员变量和成员方法仍然可以正常使用,继承分为:单继承和多继承。

  • 单继承:一个类继承另一个类
  • 多继承:一个类继承多个类

2.1单继承

语法:

class 类名称(父类名称):

    类的属性

    类的行为

代码示例:

#父类
class Phone:
    IMEI=None      #序列号
    producer=None  #厂商
    def call_by_4g(self):
        print('4g通话')
#子类
class Phone2022(Phone): #继承父类
    face_id=True  #面部识别
    def call_by_5g(self):
        print('5g通话')
#创建对象
phone=Phone2022()
phone.IMEI=4398799192  
print(phone.IMEI)  
print(phone.face_id)
phone.call_by_4g()
phone.call_by_5g()

输出:

4398799192
True
4g通话
5g通话

在代码中可以看出,父类中的成员变量IMEI和成员方法call_by_4g在子类对象phone中仍然能正常使用。

2.2多继承

语法:

class 类名称(父类1名称,父类2名称,……,父类n名称):

    类的属性

    类的行为

多继承的多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右)为优先级,即:先继承的保留,后继承的被覆盖。

代码示例:

class NFCReader:
    nfc_type='第五代'
    producer ='HM'
    def read_card(self):
        print('读取NFC卡')
    def write_card(self):
        print('写入NFC卡')
class RomoteControl:
    rc_type='红外遥控'
    def control(self):
        print('红外遥控开启')
class Phone:
    IMEI=None      #序列号
    producer=None  #厂商
    def call_by_4g(self):
        print('4g通话')

class Phone2022(Phone,NFCReader,RomoteControl): #继承多个父类,用逗号隔开
    face_id=True  #面部识别
    def call_by_5g(self):
        print('5g通话')
#创建对象
phone=Phone2022()
print(phone.producer)
phone.read_card()
phone.control()

输出:

None
读取NFC卡
红外遥控开启

第一个类NFCReader中成员变量producer ='HM',第三个类Phone中成员变量producer=None,这两个类中都有名为producer的成员变量,并且Phone2022将这两个类都继承了,打印输出phone.producer,输出“None”,因为Phone2022在继承多个父类的时候,父类Phone在左,父类NFCReader在右,原则是先继承的保留,后继承的被覆盖。

2.3复写

子类继承父类的成员属性和成员方法后,如果对其“不满意”,可以进行复写,即:在子类中重新定义同名的属性或方法即可

代码示例:

class Phone:
    IMEI=None      #序列号
    producer=None  #厂商
    def call_by_4g(self):
        print('4g通话')

class Phone2022(Phone):
    IMEI=23552321232
    producer='H'
    face_id=True  #面部识别
    def call_by_5g(self):
        print('5g通话')
        print(self.IMEI)
        print(self.producer)
#创建对象
phone=Phone2022()
phone.call_by_5g()

输出:

5g通话
23552321232
H

一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员。如果需要使用(被复写过的)原父类成员,则需要特殊的调用方式 ,有2种方式:

方式一  调用父类成员:

使用成员变量:父类名 . 成员变量
使用成员方法:父类名 . 成员方法 (self)

方式二  使用 super() 调用父类成员:

使用成员变量: super(). 成员变量
使用成员方法: super(). 成员方法 ()

代码示例:

class Phone:
    IMEI=4349854994      #序列号
    producer='M'  #厂商
    def call_by_4g(self):
        print('4g通话')

class Phone2022(Phone):
    IMEI=23552321232
    producer='H'
    face_id=True  #面部识别
    def call_by_5g(self):
        print('5g通话')
        # 方式一调用父类成员
        print(f'序列号为{Phone.IMEI}')
        # 方式二调用父类成员
        print(f'厂商为{super().producer}')
#创建对象
phone=Phone2022()
phone.call_by_5g()

输出:

5g通话
序列号为4349854994
厂商为M

注意:只可以在子类内部调用(被复写过的)原父类成员,子类的实体类对象调用默认是调用子类复写的。

三、多态

多态:多种状态,即完成某个行为时,使用不同的对象会得到不同的状态。

同样的行为(函数),传入不同的对象,得到不同的状态,如下列代码所示:

class Animal:
    def speak(self):
        pass  #pass 是占位语句,用来保证函数(方法)或类定义的完整性,表示无内容,空的意思
class Dog(Animal):
    def speak(self):
        print('汪汪汪')
class Cat(Animal):
    def speak(self):
        print('喵喵喵')

def make_noise(animal:Animal):
    animal.speak()

dog=Dog()
cat=Cat()

make_noise(dog)
make_noise(cat)

输出:

汪汪汪
喵喵喵

父类 Animal 的 speak 方法,是空实现,这种设计的含义是:

  • 父类用来确定有哪些方法
  • 具体的方法实现,由子类自行决定

这种写法,就叫做抽象类(也可以称之为接口)

  • 抽象类:含有抽象方法的类称之为抽象类
  • 抽象方法:方法体是空实现的( pass )称之为抽象方法

抽象类多用于做顶层设计(设计标准),以便子类做具体实现,也是对子类的一种软性约束,要求子类必须复写(实现)父类的一些方法
并配合多态使用,获得不同的工作状态。

上一篇:Leecode SQL 197. Rising Temperature 日期差用 DATEDIFF


下一篇:[Linux] Linux 的进程如何调度——Linux的 O(1)进程调度算法