本文最后更新于 2024-07-06,文章内容可能已经过时。

7. Python面向对象

1. 面向对象介绍

生活中或是程序中,我们都可以使用设计表格、生产表格、填写表格的形式组织数据

进行对比,在程序中:

  • 设计表格,称之为:设计类(class)
  • 打印表格,称之为:创建对象
  • 填写表格,称之为:对象属性赋值

image-20240309160014710

2. 类的定义

类中:不仅可以定义属性用来记录数据,也可以定义函数,用来记录行为

  • 类中定义的属性(变量),我们称之为:成员变量
  • 类中定义的行为(函数),我们称之为:成员方法

创建类对象语法:对象 = 类名称()

成员方法的定义:

在类中定义成员方法和定义函数基本一致,但仍有细微区别:

image-20240309161516220

self关键字是成员方法定义的时候,必须填写的。

  • 它用来表示类对象自身的意思
  • 当我们使用类对象调用方法的是,self会自动被python传入
  • 在方法内部,想要访问类的成员变量,必须使用self

注意:self关键字,尽管在参数列表中,但是传参的时候可以忽略它。

3.面向对象编程定义

面向对象包含3大主要特性:封装、继承、多态

面向对象编程就是,使用对象进行编程。即,设计类,基于类创建对象,并使用对象来完成具体的工作

4. 构造方法(相当于Java中的构造器)

Python类可以使用:__init__()方法,称之为构造方法,来实现:

  • 在创建类对象(构造类)的时候,会自动执行。
  • 在创建类对象(构造类)的时候,将传入参数自动传递给_init_方法使用。

注意:

  • 不要忘记init前后都有2个下划线
  • 构造方法也是成员方法,不要忘记在参数列表中提供:self
  • 在构造方法内定义成员变量,也需要使用self关键字。因为:变量是定义在构造方法内部,如果要成为成员变量,需要用self来表示。
"""
演示类的构造方法
"""
# 演示使用构造方法对成员变量进行赋值
# 构造方法的名称:__init__

class Student:
    def __init__(self, name, age ,tel):
        self.name = name
        self.age = age
        self.tel = tel
        print("Student类创建了一个类对象")

stu = Student("周杰轮", 31, "18500006666")
print(stu.name)
print(stu.age)
print(stu.tel)

5. 类的内置方法(魔术方法)

上文学习的_init_ 构造方法,是Python类内置的方法之一。这些内置的类方法,各自有各自特殊的功能,这些内置方法我们称之为:魔术方法。常用的几种有:

image-20240309163231278

_str_ 字符串方法:(类似Java中的toString()方法)

当类对象需要被转换为字符串之时,会输出如上结果(内存地址)。但内存地址没有多大作用,我们可以通过_str_方法,控制类转换为字符串的行为。

image-20240309163555528

_lt_ 小于符号比较方法(相当于Java的比较器comparetor)

Python中直接对2个类对象进行比较是不可以的,但是在类中实现_lt方法,即可同时完成:小于符号 和 大于符号 2种比较

注意:比较大于符号的魔术方法是:_gt__不过,实现了__lt__,__gt__就没必要实现了

_le_ 小于等于比较符号方法

魔术方法:_le_可用于:<=、>=两种比较运算符上。

注意:>=符号实现的魔术方法是 _ge不过,实现了__le__,_ge__就没必要实现了

_eq_,比较运算符实现方法

不实现eq方法,对象之间可以比较,但是是比较内存地址,也即是:不同对象==比较一定是False结果。实现了_eq_方法后,就可以按对2个对象进行值的比较了。

| 方法 | 功能 |
| ——— | ———————————————- |
| _init | 构造方法,可用于创建类对象的时候设置初始化行为 |
| _
str | 用于实现类对象转字符串的行为 |
| _lt | 用于2个类对象进行小于或大于比较 |
| _
le
| 用于2个类对象进行小于等于或大于等于比较 |
| _eq_ | 用于2个类对象进行相等比较 |

"""
演示Python内置的各类魔术方法
"""
class Student:
    def __init__(self, name, age):
        self.name = name        # 学生姓名
        self.age = age          # 学生年龄

    # __str__魔术方法
    def __str__(self):
        return f"Student类对象,name:{self.name}, age:{self.age}"

    # __lt__魔术方法
    def __lt__(self, other):
        return self.age < other.age

    # __le__魔术方法
    def __le__(self, other):
        return self.age <= other.age

    # __eq__魔术方法
    def __eq__(self, other):
        return self.age == other.age

stu1 = Student("周杰轮", 31)
stu2 = Student("林俊节", 36)
print(stu1 == stu2)

6. 面向对象——封装

封装表示的是,将现实世界事物的:属性、行为封装到类中(私有化),并描述为:成员变量、成员方法,从而完成程序对现实世界事物的描述。

image-20240309165300031

私有成员

类中提供了私有成员的形式来支持:私有成员变量和私有成员方法。定义:

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

使用私有成员(类似通过java的get和set方法)

私有方法无法直接被类对象使用,私有变量无法赋值,也无法获取值。但是可以被类中的其它成员可以访问私有成员。

image-20240309170227902

7. 面向对象——继承

继承表示:从父类那里继承(复制)成员变量和成员方法(不含私有)。继承分为:单继承和多继承。

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

class 类名(父类)

​ 类的内容体

多继承:一个类继承多个类,按照顺序从左向右依次继承

Python的类之间也支持多继承,即一个类,可以继承多个父类。

class 类名(父类1, 父类2, ……, 父类N)

​ 类的内容体

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

pass关键字:pass是占位语句,用来保证函数(方法)或类定义的完整性,表示无内容,空的意思。(如继承父类的类的内容体不能为空,此时可通过pass占位,防止报错。

复写:

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

image-20240309174833885

调用父类同名成员

一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员如果需要使用被复写的父类的成员,需要特殊的调用方式:

  • 方式一:调用父类成员
  • 使用成员变量:父类名.成员变量
  • 使用成员方法:父类名.成员方法(self)
  • 方式2:使用super()调用父类成员
  • 使用成员变量:super().成员变量
  • 使用成员方法:super().成员方法()

注意:

  • 只能在子类内调用父类的同名成员。子类的类对象直接调用会调用子类复写的成员。
  • super()不适用情况:多个父类中存在同名函数。
"""
演示面向对象:继承中
对父类成员的复写和调用
"""
class Phone:
    IMEI = None             # 序列号
    producer = "ITCAST"     # 厂商
    def call_by_5g(self):
        print("使用5g网络进行通话")

# 定义子类,复写父类成员
class MyPhone(Phone):
    producer = "ITHEIMA"        # 复写父类的成员属性
    def call_by_5g(self):
        print("开启CPU单核模式,确保通话的时候省电")
        # 方式1
        # print(f"父类的厂商是:{Phone.producer}")
        # Phone.call_by_5g(self)
        # 方式2
        print(f"父类的厂商是:{super().producer}")
        super().call_by_5g()
        print("关闭CPU单核模式,确保性能")

phone = MyPhone()
phone.call_by_5g()
print(phone.producer)

8. 类型注解

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

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

主要功能:

  • 帮助第三方IDE工具(如PyCharm)对代码进行类型推断,协助做代码提示

  • 帮助开发者自身对变量进行类型注释

支持:

  • 变量的类型注解
  • 函数(方法)形参列表和返回值的类型注解

变量型注解

  • 基础语法: 变量: 类型
  • 在注释中进行类型注解: # type: 类型

提示:

  • 为变量设置注解,显示的变量定义,一般无需注解。(就算不写注解,也明确的知晓变量的类型)
  • 一般,无法直接看出变量类型之时会添加变量的类型注解

类型注解的限制:类型注解并不会真正的对类型做验证和判断。也就是,类型注解仅仅是提示性的,不是决定性的

"""
演示变量的类型注解
"""
# 基础数据类型注解
import json
import random

var_1: int = 10
var_2: str = "itheima"
var_3: bool = True
# 类对象类型注解
class Student:
    pass
stu: Student = Student()

# 基础容器类型注解
my_list1: list = [1, 2, 3]
my_tuple1: tuple = (1, 2, 3)
my_dict1: dict = {"itheima": 666}
# 容器类型详细注解
my_list: list[int] = [1, 2, 3]
my_tuple: tuple[int, str, bool] = (1, "itheima", True)
my_dict: dict[str, int] = {"itheima": 666}
# 在注释中进行类型注解
var_4 = random.randint(1, 10)   # type: int
var_5 = json.loads('{"name": "zhangsan"}')  # type: dict[str, str]
def func():
    return 10
var_6 = func()  # type: int
# 类型注解的限制:如下代码,注解错了但没有报错
var_7: int = "itheima"
var_8: str = 123

函数(方法)的类型注解

形参注解

在调用函数(方法),传入参数的时候,工具无法提示参数类型这些都是因为,我们在定义函数(方法)的时候,没有给形参进行注解。

def 函数方法名(形参名:类型,形参名:类型,……)

返回值注解

返回值类型注解的符号使用: ->

"""
演示对函数(方法)进行类型注解
"""
# 对形参进行类型注解
def add(x: int, y: int):
    return x + y

# 对返回值进行类型注解
def func(data: list) -> list:
    return data

print(func(1))

Union类型

使用Union[类型, ……, 类型] , 可以定义联合类型注解。

Union联合类型注解,在变量注解、函数(方法)形参和返回值注解中,均可使用。

Union的使用方式

导包:from typing import Union

使用:Union[类型, ……, 类型]

9. 面向对象——多态

多态,指的是:多种状态,即完成某个行为时,使用不同的对象会得到不同的状态。(同样的行为(函数),传入不同的对象,得到不同的状态)

多态常作用在继承关系上。比如:

  • 函数(方法)形参声明接收父类对象
  • 实际传入父类的子类对象进行工作,即:
  • 以父类做定义声明
  • 以子类做实际工作
  • 用以获得同一行为, 不同状态

抽象类(接口)

父类用来确定有哪些方法。具体的方法实现,由子类自行决定。这种写法,就叫做抽象类(也可以称之为接口)。抽象类就好比定义一个标准,包含了一些抽象的方法,要求子类必须实现。

  • 抽象类:含有抽象方法的类称之为抽象类

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

配合多态,完成:

  • 抽象的父类设计(设计标准)
  • 具体的子类实现(实现标准)

抽象类的作用:

  • 多用于做顶层设计(设计标准),以便子类做具体实现。
  • 也是对子类的一种软性约束,要求子类必须复写(实现)父类的一些方法并配合多态使用,获得不同的工作状态。
"""
演示面向对象的多态特性以及抽象类(接口)的使用
"""
class Animal:
    def speak(self):
        pass
class Dog(Animal):
    def speak(self):
        print("汪汪汪")
class Cat(Animal):
    def speak(self):
        print("喵喵喵")
def make_noise(animal: Animal):
    """制造点噪音,需要传入Animal对象"""
    animal.speak()

# 演示多态,使用2个子类对象来调用函数
dog = Dog()
cat = Cat()

make_noise(dog)
make_noise(cat)

# 演示抽象类
class AC:
    def cool_wind(self):
        """制冷"""
        pass

    def hot_wind(self):
        """制热"""
        pass

    def swing_l_r(self):
        """左右摆风"""
        pass
class Midea_AC(AC):
    def cool_wind(self):
        print("美的空调制冷")

    def hot_wind(self):
        print("美的空调制热")

    def swing_l_r(self):
        print("美的空调左右摆风")
class GREE_AC(AC):
    def cool_wind(self):
        print("格力空调制冷")

    def hot_wind(self):
        print("格力空调制热")

    def swing_l_r(self):
        print("格力空调左右摆风")
def make_cool(ac: AC):
    ac.cool_wind()

midea_ac = Midea_AC()
gree_ac = GREE_AC()

make_cool(midea_ac)
make_cool(gree_ac)