# 7. Python面向对象 ### 1. 面向对象介绍 生活中或是程序中,我们都可以使用设计表格、生产表格、填写表格的形式组织数据 进行对比,在程序中: \* 设计表格,称之为:设计类(class) \* 打印表格,称之为:创建对象 \* 填写表格,称之为:对象属性赋值 ![image-20240309160014710](https://hgh-typora-image.oss-cn-guangzhou.aliyuncs.com/img/image-20240309160014710.png) ### 2. 类的定义 类中:不仅可以定义属性用来记录数据,也可以定义函数,用来记录行为 \* 类中定义的属性(变量),我们称之为:\*\*成员变量\*\* \* 类中定义的行为(函数),我们称之为:\*\*成员方法\*\* 创建类对象语法:对象 = 类名称() \*\*成员方法的定义:\*\* 在类中定义成员方法和定义函数基本一致,但仍有细微区别: ![image-20240309161516220](https://hgh-typora-image.oss-cn-guangzhou.aliyuncs.com/img/image-20240309161516220.png) self关键字是成员方法定义的时候,必须填写的。 \* 它用来表示类对象自身的意思 \* 当我们使用类对象调用方法的是,self会自动被python传入 \* 在方法内部,想要访问类的成员变量,必须使用self \*\*注意:\*\*self关键字,尽管在参数列表中,但是传参的时候可以忽略它。 ### 3.面向对象编程定义 面向对象包含3大主要特性:封装、继承、多态 面向对象编程就是,使用对象进行编程。即,设计类,基于类创建对象,并使用对象来完成具体的工作 ### 4. 构造方法(相当于Java中的构造器) Python类可以使用:\*\*\\__init\\_\\_()方\*\*法,称之为构造方法,来实现: \* 在创建类对象(构造类)的时候,会自动执行。 \* 在创建类对象(构造类)的时候,将传入参数自动传递给\\__init__方法使用。 \*\*注意:\*\* \* 不要忘记init前后都有2个下划线 \* 构造方法也是成员方法,不要忘记在参数列表中提供:self \* 在构造方法内定义成员变量,也需要使用self关键字。因为:变量是定义在构造方法内部,如果要成为成员变量,需要用self来表示。 \`\`\`python """ 演示类的构造方法 """ # 演示使用构造方法对成员变量进行赋值 # 构造方法的名称:__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](https://hgh-typora-image.oss-cn-guangzhou.aliyuncs.com/img/image-20240309163231278.png) \*\*\\__str__ 字符串方法\*\*:(类似Java中的toString()方法) 当类对象需要被转换为字符串之时,会输出如上结果(内存地址)。但内存地址没有多大作用,我们可以通过\\__str__方法,控制类转换为字符串的行为。 ![image-20240309163555528](https://hgh-typora-image.oss-cn-guangzhou.aliyuncs.com/img/image-20240309163555528.png) \*\*\\__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 """ 演示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](https://hgh-typora-image.oss-cn-guangzhou.aliyuncs.com/img/image-20240309165300031.png) \*\*私有成员\*\* 类中提供了私有成员的形式来支持:私有成员变量和私有成员方法。定义: \* 私有成员变量:变量名以\\__开头(2个下划线) \* 私有成员方法:方法名以__开头(2个下划线) \*\*使用私有成员\*\*(类似通过java的get和set方法) 私有方法无法直接被类对象使用,私有变量无法赋值,也无法获取值。但是可以被类中的其它成员可以访问私有成员。 ![image-20240309170227902](https://hgh-typora-image.oss-cn-guangzhou.aliyuncs.com/img/image-20240309170227902.png) ### 7. 面向对象------继承 继承表示:从父类那里继承(复制)成员变量和成员方法(不含私有)。继承分为:单继承和多继承。 \*\*单继承:\*\*一个类继承另一个类 \> class 类名(父类) \> \> 类的内容体 \*\*多继承:\*\*一个类继承多个类,按照顺序从左向右依次继承 Python的类之间也支持多继承,即一个类,可以继承多个父类。 \> class 类名(父类1, 父类2, ......, 父类N) \> \> 类的内容体 \*\*注意:\*\*多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右)为优先级。即:先继承的保留,后继承的被覆盖。 \*\*pass关键字:\*\*pass是占位语句,用来保证函数(方法)或类定义的完整性,表示无内容,空的意思。(如继承父类的类的内容体不能为空,此时可通过pass占位,防止报错。 \*\*复写:\*\* 子类继承父类的成员属性和成员方法后,如果对其"不满意",那么可以进行复写。即:在子类中重新定义同名的属性或方法即可。 ![image-20240309174833885](https://hgh-typora-image.oss-cn-guangzhou.aliyuncs.com/img/image-20240309174833885.png) \*\*调用父类同名成员\*\*: 一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员如果需要使用被复写的父类的成员,需要特殊的调用方式: \* 方式一:调用父类成员 \* 使用成员变量:\*\*父类名.成员变量\*\* \* 使用成员方法:\*\*父类名.成员方法(self)\*\* \* 方式2:使用super()调用父类成员 \* 使用成员变量:\*\*super().成员变量\*\* \* 使用成员方法:\*\*super().成员方法()\*\* \*\*注意:\*\* \* 只能在子类内调用父类的同名成员。子类的类对象直接调用会调用子类复写的成员。 \* super()不适用情况:多个父类中存在同名函数。 \`\`\`python """ 演示面向对象:继承中 对父类成员的复写和调用 """ 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: 类型 \*\*提示:\*\* \* 为变量设置注解,显示的变量定义,一般无需注解。(就算不写注解,也明确的知晓变量的类型) \* 一般,无法直接看出变量类型之时会添加变量的类型注解 \*\*类型注解的限制:\*\*类型注解并不会真正的对类型做验证和判断。也就是,类型注解仅仅是提示性的,不是决定性的 \`\`\`python """ 演示变量的类型注解 """ # 基础数据类型注解 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 函数方法名(形参名:类型,形参名:类型,......) \*\*返回值注解\*\* 返回值类型注解的符号使用: \*\*-\>\*\* \`\`\`python """ 演示对函数(方法)进行类型注解 """ # 对形参进行类型注解 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)称之为抽象方法 配合多态,完成: \* 抽象的父类设计(设计标准) \* 具体的子类实现(实现标准) \*\*抽象类的作用:\*\* \* 多用于做顶层设计(设计标准),以便子类做具体实现。 \* 也是对子类的一种软性约束,要求子类必须复写(实现)父类的一些方法并配合多态使用,获得不同的工作状态。 \`\`\`python """ 演示面向对象的多态特性以及抽象类(接口)的使用 """ 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) \`\`\`