10.Python 对象(封装、继承、多态、类型注解)

一、初始对象

  • 在程序中是可以做到和生活中那样,设计表格、生产表格、填写表格的组织形式的
    1. 在程序中设计表格,称之为设计类(class)
    class Student:
        name: None
    
    1. 在程序中打印生产表格,称之为创建对象
    stu1 = Student()
    stu2 = Student()
    
    1. 在程序中填写表格,称之为对象属性赋值
    stu1.name = "jack"
    stu2.name = "tom"
    

    二、类与对象

    1、类的定义和使用
    1. 类的定义
    class 【类】:
        【类的属性】
    
        【类的行为】
    
    说明
    class 关键字,表示要定义类
    类的属性 定义在类中的变量(成员变量)
    类的行为 定义在类中的函数(成员方法)
    1. 创建类对象
    【对象】 = 类()
    
    1. 成员方法的定义
    def 【方法】(self, 【形参 1】, 【形参 2】...):
        【方法体】
    
  • 在方法定义的参数列表中,有一个 self 关键字,它是成员方法定义的时必须填写的,用来表示类对象自身的意思,当使用类对象调用方法的是,self 会自动被 Python 传入(即可以忽略),在方法内部,想要访问类的成员变量,必须使用 self
  • 2、演示
    1. 演示代码 1
    class Student:
        name: None
    
        def sayHi(self):
            print(f"Hello,我是 {self.name}")
    
        def sayMsg(self, msg):
            print(f"Hello,{msg}")
    
    stu = Student()
    stu.name = "jack"
    stu.sayHi()
    stu.sayMsg("今天天气不错")
    
  • 输出结果
  • Hello,我是 jack
    Hello,今天天气不错
    
    1. 演示代码 2
    class Clock:
        id = None # 编号
        price = None # 价格
    
        def ring(self):
            print(f"闹钟 {self.id} 响铃了")
    
    clock1 = Clock()
    clock1.id = "001"
    clock1.price = 10
    
    clock2 = Clock()
    clock2.id = "002"
    clock2.price = 20
    
    clock1.ring()
    clock2.ring()
    
  • 输出结果
  • 闹钟 001 响铃了
    闹钟 002 响铃了
    

    三、类的成员方法

    1、构造方法
    (1)基本介绍
  • Python 类可以使用 __init__ 方法,它是构造方法
    1. 在创建类对象(构造类)的时候,构造方法会自动执行

    2. 在创建类对象(构造类)的时候,Python 将传入参数自动传递给 __init__ 方法使用

    3. 构造方法也是成员方法,不要忘记在参数列表中提供 self,变量定义在构造方法内部,如果要成为成员变量,需要用 self 来表示

    (2)演示
    class Student:
        name = None
        age = None
        address = None
    
        def __init__(self, name, age, address):
            self.name = name
            self.age = age
            self.address = address
            print("Student 类创建了一个对象")
    
        def showInfo(self):
            print(f"{self.name}, {self.age}, {self.address}")
    
    stu = Student("jack", 20, "地球")
    stu.showInfo()
    
  • 输出结果
  • Student 类创建了一个对象
    jack, 20, 地球
    
    2、其他内置方法
  • __init__ 方法,是 Python 类内置的方法之一,这些内置的类方法,各自有各自特殊的功能,这些内置方法我们称之为魔术方法
  • (1)__str__ 方法
    1. 当类对象需要被转换为字符串时,默认会输出内存地址
    class Student:
        name = None
        age = None
        address = None
    
        def __init__(self, name, age, address):
            self.name = name
            self.age = age
            self.address = address
    
    stu = Student("jack", 20, "地球")
    print(stu)
    print(str(stu))
    
  • 输出结果
  • <__main__.Student object at 0x000001DFE0D00FD0>
    <__main__.Student object at 0x000001DFE0D00FD0>
    
    1. 内存地址没有多大作用,可以通过 __str__ 方法,控制类转换为字符串的行为
    class Student:
        name = None
        age = None
        address = None
    
        def __init__(self, name, age, address):
            self.name = name
            self.age = age
            self.address = address
    
        def __str__(self):
            return f"Student: name={self.name}, age={self.age}, address={self.address}"
    
    stu = Student("jack", 20, "地球")
    print(stu)
    print(str(stu))
    
  • 输出结果
  • Student: name=jack, age=20, address=地球
    Student: name=jack, age=20, address=地球
    
    (2)__lt__ 方法
    1. 直接对 2 个对象进行比较是非法的
    class Student:
        name = None
        age = None
        address = None
    
        def __init__(self, name, age, address):
            self.name = name
            self.age = age
            self.address = address
    
    stu1 = Student("jack", 20, "地球")
    stu2 = Student("tom", 15, "地球")
    
    print(stu1 < stu2)
    print(stu1 > stu2)
    
  • 输出结果
  • TypeError: '<' not supported between instances of 'Student' and 'Student'
    
    1. 在类中实现 __lt__ 方法,可同时完成小于和大于两种比较
    class Student:
        name = None
        age = None
        address = None
    
        def __init__(self, name, age, address):
            self.name = name
            self.age = age
            self.address = address
    
        def __lt__(self, other):
            return self.age < other.age
    
    stu1 = Student("jack", 20, "地球")
    stu2 = Student("tom", 15, "地球")
    
    print(stu1 < stu2)
    print(stu1 > stu2)
    
  • 输出结果
  • False
    True
    
    (3)__gt__ 方法
  • 比较大于符号的是 __gt__ 方法,不过,实现了 __lt__ 方法,__gt__ 方法就没必要实现了
  • (4)__le__ 方法
  • __le__ 方法可用于小于等于和大于等于两种比较上
  • class Student:
        name = None
        age = None
        address = None
    
        def __init__(self, name, age, address):
            self.name = name
            self.age = age
            self.address = address
    
        def __le__(self, other):
            return self.age <= other.age
    
    stu1 = Student("jack", 20, "地球")
    stu2 = Student("tom", 15, "地球")
    
    print(stu1 <= stu2) # False
    print(stu1 >= stu2) # True
    
    (5)__ge__ 方法
  • 比较大于等于符号的是 __ge__ 方法,不过,实现了 __le__ 方法,__ge__ 方法就没必要实现了
  • (6)__eq__ 方法
    1. 对象之间可以比较,但是默认是比较内存地址,也就是不同对象比较结果一定是 False
    class Student:
        name = None
        age = None
        address = None
    
        def __init__(self, name, age, address):
            self.name = name
            self.age = age
            self.address = address
    
    stu3 = Student("mary", 30, "地球")
    stu4 = Student("smith", 30, "地球")
    
    print(stu1 == stu2)
    
  • 输出结果
  • False
    
    1. 实现 __eq__ 方法就可以按照自己的方式来决定两个对象是否相等
    class Student:
        name = None
        age = None
        address = None
    
        def __init__(self, name, age, address):
            self.name = name
            self.age = age
            self.address = address
    
    stu3 = Student("mary", 30, "地球")
    stu4 = Student("smith", 30, "地球")
    
    print(stu1 == stu2)
    
  • 输出结果
  • True
    

    四、封装

    1、基本介绍
  • 封装表示的是将现实世界事物的属性和行为封装到类中,描述为成员变量和成员方法,从而完成程序对现实世界事物的描述

  • 现实世界中的事物,有属性和行为,但是不代表这些属性和行为都是开放给用户使用的,例如,苹果越狱、安卓 root,也是为了突破权限使用这些对用户隐藏的属性和行为

  • 现实事物有不公开的属性和行为,作为现实事物在程序中映射的类也支持,类中提供了私有成员的形式来支持,它们分别是私有成员变量和私有成员方法

    1. 定义私有成员变量:变量名以两个下划线(__)开头

    2. 定义私有成员方法:方法名以两个下划线(__)开头

  • 私有变量无法赋值,也无法获取值,私有方法无法直接被类对象使用
  • 2、演示
    class Phone:
        IMEI = None # 序列号
        producer = None # 厂商
        __current_volt = None # 当前电压
    
        def call_by_5g(self):
            print("开启 5G 通信")
    
        def __keep_single_core(self):
            print("让 CPU 以单核模式运行以节省电量")
    
    1. 使用私有成员变量
    phone = Phone()
    
    phone.__keep_single_core()
    
  • 输出结果
  • AttributeError: 'Phone' object has no attribute '__keep_single_core'
    
    1. 使用私有成员方法
    phone = Phone()
    
    print(phone.__current_volt)
    
  • 输出结果
  • AttributeError: 'Phone' object has no attribute '__current_volt'
    
    3、补充
    (1)补充说明
    1. 私有成员无法被类对象使用,但是可以被其它的成员使用

    2. 可以对类对象的与私有成员属性同名的属性赋值,但这是相当于又定义了一个新的变量,类内部的私有成员变量值不变

    (2)演示
    class Phone:
        IMEI = None # 序列号
        producer = None # 厂商
        __current_volt = 10 # 当前电压
    
        def call_by_5g(self):
            if self.__current_volt >= 1:
                self.__keep_single_core()
                print("开启 5G 通信")
            else:
                print("通信失败,电量不足")
    
        def __keep_single_core(self):
            print("让 CPU 以单核模式运行以节省电量")
    
        def show_current_volt(self):
            print(self.__current_volt)
    
    phone = Phone()
    
    phone.__current_volt = 100
    print(phone.__current_volt)
    
    phone.show_current_volt()
    phone.call_by_5g()
    
  • 输出结果
  • 100
    10
    让 CPU 以单核模式运行以节省电量
    开启 5G 通信
    

    五、继承

    1、基本介绍
  • 继承就是从父类那里继承(复制)来成员变量和成员方法(不含私有成员),继承分为单继承和多继承
  • class 【类】(【父类】):
        【类体】
    
    class 【类】(【父类 1】, 【父类 2】):
        【类体】
    
  • 注:多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右)为优先级,即先继承的保留,后继承的被丢弃
  • 2、演示
    class Phone:
        IMEI = None  # 序列号
        producer = None  # 厂商
    
        def call_by_4g(self):
            print("开启 4G 通信")
    
    class NFCReader:
        nfc_type = "第五代"
        producer = "HM"
    
        def read_card(self):
            print("读取 NFC 卡")
    
        def write_card(self):
            print("写入 NFC 卡")
    
    class RemoteControl:
        rc_type = "红外遥控"
    
        def control(self):
            print("开启红外遥控")
    
    class Phone2023(Phone, NFCReader, RemoteControl):
        face_id = True # 面部识别
    
        def call_by_5g(self):
            print("开启 5G 通信")
    
    phone2023 = Phone2023()
    
    phone2023.call_by_4g()
    phone2023.call_by_5g()
    phone2023.read_card()
    phone2023.write_card()
    
    print(phone2023.producer)
    
  • 输出结果
  • 开启 4G 通信
    开启 5G 通信
    读取 NFC 卡
    写入 NFC 卡
    None
    
    3、复写和使用父类成员
    (1)基本介绍
  • 子类继承父类的成员属性和成员方法后,可以进行复写,一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员

  • 如果需要使用被复写的父类的成员,需要特殊的调用方式

    1. 调用父类成员
    【父类】.【成员变量】
    
    【父类】.【成员方法】(self)
    
    1. 使用 super() 调用父类成员
    super().【成员变量】
    
    super().【成员方法】()
    
    (2)演示
    class Phone:
        IMEI = None
        producer = "MY_PHONE"
    
        def call_by_5g(self):
            print("开启父类的 5G 通信")
    
    class Phone2023(Phone):
        producer = "MY_PHONE_2023"
    
        def call_by_5g(self):
            print("开启子类的 5G 通信")
    
        def run_by_father(self):
            print(super().producer)
            super().call_by_5g()
    
    phone2023 = Phone2023()
    
    print(phone2023.producer)
    phone2023.call_by_5g()
    phone2023.run_by_father()
    
  • 输出结果
  • MY_PHONE_2023
    开启子类的 5G 通信
    MY_PHONE
    开启父类的 5G 通信
    

    六、类型注解

    1、类型注解引入
  • Python 在 3.5 版本的时候引入了类型注解,以方便静态类型检查工具、IDE 等第三方工具

  • 类型注解就是在代码中涉及数据交互的地方提供数据类型的注解(显式的说明)

  • 类型注解的主要功能就是帮助第三方 IDE 工具对代码进行类型推断,协助做代码提示,帮助开发者自身对变量进行类型注释

  • 类型注解支持变量的类型注解和函数形参列表和返回值的类型注解

  • 2、变量的类型注解
    (1)基本注解
    【变量】: 【类型】
    
    1. 基础数据类型注解
    var_1: int = 10
    var_2: float = 3.1415926
    var_3: bool = True
    var_4: str = "Hello World"
    
    1. 类对象类型注解
    class Student:
        name = None
        age = None
    
    stu: Student = Student()
    
    1. 基础容器类型注解
    my_list: list = [1, 2, 3]
    my_tuple: tuple = (1, 2, 3)
    my_set: set = {1, 2, 3}
    my_dict: dict = {"a1": 123}
    my_str: str = "Hello World"
    
    1. 容器类型详细注解
    m_list: list[int] = [1, 2, 3]
    m_tuple: tuple[str, int, bool] = ("Hello", 10, True)
    m_set: set[int] = {1, 2, 3}
    m_dict: dict[str, int] = {"a1", 100}
    
    (2)注释中的注解
    # type: 【类型】
    
    class Student:
        name = None
        age = None
    
    def func():
        return Student()
    
    a = random.randint(1, 10) # type: int
    b = json.loads('{"a1": 100}') # type: dict[str, 100]
    c = func() # type: Student
    
    (3)补充
    1. 为变量设置注解,显示的变量定义,一般无需注解,因为就算不写注解,也明确的知晓变量的类型,例如
    var_1: int = 10
    var_2: float = 3.1415926
    var_3: bool = True
    var_4: str = "Hello World"
    
    1. 一般,无法直接看出变量类型时会添加变量的类型注解,例如
    a = random.randint(1, 10) # type: int
    b = json.loads('{"a1": 100}') # type: dict[str, 100]
    
    1. 类型注解并不会真正的对类型做验证和判断,也就是说,类型注解仅仅是提示性的,不是决定性的,例如,如下代码不会报错
    n1: int = "abc"
    n2: str = 200
    
    2、函数的类型注解
  • 函数的形参和返回值都可以添加类型注解
  • def 【函数】(【形参】: 【类型】, 【形参】: 【类型】)  -> 【返回值类型】:
        【函数体】
    
    def add(x: int, y: int) -> int:
        return x + y
    
    def addAll(data: list[int]) -> int:
        sum = 0;
        for x in data:
            sum += x
        return sum
    
    res1: int = add(1, 2)
    res2: int = addAll([1, 2, 3, 4, 5])
    
    3、联合类型注解
    (1)问题引入
    1. 如下代码的类型注解可以很容易定义
    my_list1: list[int] = [1, 2, 3]
    my_dict1: dict[str, int] = {"id": 10, "age": 20}
    
    1. 如下的代码的类型注解却不好定义
    my_list2 = [1, 2, "Hello World"]
    my_dict2 = {"name": "jack", "age": 20}
    
    (2)解决方案
    Union[【类型】, 【类型】]
    
  • 联合类型注解,在变量注解、函数的形参和返回值注解中,均可使用
  • my_list3: list[Union[str, int]] = [1, 2, "Hello World"]
    my_dict3: dict[str, Union[str, int]] = {"name": "jack", "age": 20}
    
    def fn(data: Union[str, int]) -> Union[str, int]:
        if type(data) == int:
            return data * 10
        if type(data) == str:
            return  data + data
    
    print(fn(1)) # 10
    print(fn("Hello")) # HelloHello
    

    七、多态

    1、基本介绍
  • 多态指的是多种状态,即完成某个行为时,使用不同的对象会得到不同的状态,例如,实现某个功能的函数,传入不同的对象,得到不同的状态

  • 多态常作用在继承关系上,例如,函数形参声明接收父类对象,实际传入父类的子类对象进行工作,即,以父类做定义声明,以子类做实际工作,用以获得同一行为,不同状态

  • 2、演示
    class Animal:
        def speak(self):
            pass
    
    class Dog(Animal):
        def speak(self):
            print("小狗汪汪叫")
    
    class Cat(Animal):
        def speak(self):
            print("小猫喵喵叫")
    
    def make_speak(animal: Animal):
        animal.speak()
    
    animal = Animal()
    dog = Dog()
    cat = Cat()
    
    make_speak(animal)
    make_speak(dog)
    make_speak(cat)
    
  • 输出结果
  • 小狗汪汪叫
    小猫喵喵叫
    
    3、抽象类
    (1)基本介绍
  • 上述父类 Animal 的 speak 方法,是空实现(pass),这种设计的含义是父类用来确定有哪些方法,但具体的方法实现由子类自行决定,这种写法称之为抽象类(接口)
    1. 抽象类:含有抽象方法的类称之为抽象类

    2. 抽象方法:方法体是空实现的(pass)方法称之为抽象方法

    (2)演示
    class Person:
        def eat(self):
            pass
    
        def work(self):
            pass
    
    class Teacher(Person):
        def eat(self):
            print("老师去教室食堂用餐")
    
        def work(self):
            print("老师开始教书")
    
    class Worker(Person):
        def eat(self):
            print("工人去工地食堂用餐")
    
        def work(self):
            print("工人开始劳作")
    
    teacher = Teacher()
    worker = Worker()
    
    teacher.eat()
    teacher.work()
    
    worker.eat()
    teacher.work()
    
  • 输出结果
  • 老师去教室食堂用餐
    老师开始教书
    工人去工地食堂用餐
    老师开始教书
    

    作者:我命由我12345

    物联沃分享整理
    物联沃-IOTWORD物联网 » 10.Python 对象(封装、继承、多态、类型注解)

    发表回复