十、Python面向对象
在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。
1. 类的定义和使用
-
类的定义
class Student: # 成员变量 name = None # 成员方法 self 必须存在 不占用参数的位置 def say_hi(self): print(f'大家好,我叫{self.name}')
- class是关键字,表示要定义类了
- 类的属性,即定义在类中的变量(成员变量)
- 类的行为,即定义在类中的函数(成员方法)
-
创建类对象的语法:
s1 = Student()
2. 成员变量和成员方法
-
类中定义的属性(变量),我们称之为:成员变量
-
类中定义的行为(函数),我们称之为:成员方法
- self关键字是成员方法定义的时候,必须填写的
- 它用来表示类对象自身的意思
- 当我们使用类对象调用方法的是,self会自动被python传入
- 在方法内部,想要访问类的成员变量,必须使用self
- self关键字,尽管在参数列表中,但是传参的时候可以忽略它
class Student:
# 成员变量
name = None
# 成员方法 self 必须存在 不占用参数的位置
def say_hi(self):
print(f'大家好,我叫{self.name}')
def say_hi1(self, msg):
print(f'大家好,我是{self.name},{msg}')
s1 = Student()
s1.say_hi() # 调用的时候无需传参
s1.say_hi1('爱运动') # 调用的时候需要传msg参数
3. 类和对象
基于类创建对象的语法:
对象名 = 类名称()
-
为什么非要创建对象才能使用呢?
-
类只是一种程序内的“设计图纸”,需要基于图纸生产实体(对象),才能正常工作这种套路,称之为:面向对象编程
-
设计类,基于类创建对象,由对象做具体的工作
-
4. 构造方法
Python类可以使用:
__init__()
方法,称之为构造方法。
- 在创建类对象(构造类)的时候,会自动执行。
- 在创建类对象(构造类)的时候,将传入参数自动传递给
__init__
方法使用。
class Student:
# 以下属性可以省略
name = None
age = None
tel = None
def __init__(self, name, age, tel):
self.name = name
self.age = age
self.tel = tel
print('构造了一个Stdent')
s1 = Student('xzq', 23, '15011112222')
print(f'{s1.name},{s1.age},{s1.tel}')
- 注意
- 构造方法也是成员方法,不要忘记在参数列表中提供:self
- 在构造方法内定义成员变量,需要使用self关键字
5. 魔术方法
上文学习的__init__ 构造方法,是Python类内置的方法之一。
这些内置的类方法,各自有各自特殊的功能,这些内置方法我们称之为:魔术方法
方法 | 功能 |
---|---|
__init__ | 构造方法,可用于创建类对象的时候设置初始化行为 |
__str__ | 用于实现类对象转字符串的行为 |
__lt__ | 用于2个类对象进行小于或大于比较 |
__le__ | 用于2个类对象进行小于等于或大于等于比较 |
__eq__ | 用于2个类对象进行相等比较 |
- 案例:
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f'Student类对象:name:{self.name},age:{self.age}'
# 实现__lt__方法,即可同时完成:'<' 和 '>' 2种比较
def __lt__(self, other):
return self.age < other.age
# 实现__le__方法,即可同时完成:'<=' 和 '>=' 2种比较
def __le__(self, other):
return self.age <= other.age
# 不实现__eq__方法,对象之间可以比较,但是是比较内存地址,也即是:不同对象==比较一定是False结果。
# 实现了__eq__方法,就可以按照自己的想法来决定2个对象是否相等了。
def __eq__(self, other):
return self.age == other.age
s1 = Student('xzq', 23)
s2 = Student('zlo', 24)
print(str(s1)) # Student类对象:name:xzq,age:23
print(s2) # Student类对象:name:zlo,age:24
print(s1 < s2) # True
print(s1 > s2) # False
print(s1 <= s2) # True
print(s1 >= s2) # False
print(s1 == s2) # False
6. 类型注解
Python在3.5版本的时候引入了类型注解,以方便静态类型检查工具,IDE等第三方工具。
类型注解:在代码中涉及数据交互的地方,提供数据类型的注解(显式的说明)。
-
主要功能
-
帮助第三方IDE工具(如PyCharm)对代码进行类型推断,协助做代码提示
-
帮助开发者自身对变量进行类型注释
-
-
支持
-
变量的类型注解
-
语法1:
变量: 类型
# 基础数据类型注解 var_1: int = 10 var_2: float = 12.2 var_3: str = 'xzq' var_4: bool = True class Student: pass # 类对象类型注解 stu: Student = Student() # 基础容器类型注解 my_lsit: list = [1, 2, 3] my_tuple: tuple = (1, 2, 3) my_set: set = {1, 2, 3} my_dict: dict = {'name': 'xzq'} # 容器详细类型注解 my_lsit1: list[int] = [1, 2, 3] # 元组类型设置类型详细注解,需要将每一个元素都标记出来 my_tuple1: tuple[int, bool, str] = (1, True, 'cx') my_set1: set[int] = {1, 2, 3} # 字典类型设置类型详细注解,需要2个类型,第一个是key第二个是value my_dict1: dict[str, int] = {'id': 1}
-
语法2: 在注释中,
# type: 类型
# 在注释中进行类型注解 var_int = random.randint(1, 10) # type:int var_dict = json.loads('{"name":"zhanshan"}') # type:dict[str,str]
-
-
函数(方法)形参列表和返回值的类型注解
-
语法:
def 函数方法名(形参1 : 类型,...,形参n : 类型) -> 返回值类型: pass
-
案例:
# 返回值类型注解的符号使用: -> def funccc(data: list) -> list: return data
-
-
-
Union类型
- Union类型可以定义一个新类型,该类型可以包括多个其他类型
- 使用Union可以定义联合类型注解
-
使用方式
-
导包:
from typing import Union
-
使用:
Union[类型, ......, 类型]
该例子表示var_list是一个列表,列表元素可以是int型和str型的数据
var_list: list[Union[str, int]] = ['xzq', 1, 2, 'xzq']
-
7. 面向对象的三大特性
面向对象编程简单理解就是基于模板(类)去创建实体(对象),使用对象完成功能开发。
面向对象包含3大主要特性:
🐰 封装
🐇 继承
🌈 多态
-
封装
封装表示的是,将现实世界事物的属性和方法,封装到类中,描述为成员变量和成员方法,从而完成程序对现实世界事物的描述
-
私有成员
现实事物有不公开的属性和行为,那么作为现实事物在程序中映射的类,也应该支持
-
类中提供了私有成员的形式来支持。
📘 私有成员变量
🔖 私有成员方法
-
定义私有成员
😃 私有成员变量:变量名以
__
开头(2个下划线)😃 私有成员方法:方法名以
__
开头(2个下划线)class Phone: __current_voltage = 0.5 def __keep_single_core(self): print('单核运行') def call_by_5g(self): if self.__current_voltage >= 1: print('5G已打开') else: print('电量不足') phone = Phone() # phone.__keep_single_core() # 私有成员变量方法不能访问 # phone.__current_voltage phone.call_by_5g()
-
-
-
继承
-
继承分为:单继承和多继承
-
继承表示:将从父类那里继承(复制)来成员变量和成员方法**(不含私有)**
-
单继承
class 类名(父类名): 类内容体
-
多继承:继承顺序同样成员左优先继承
class 类名(父类1,父类2,父类3,...,父类n): 类内容体
-
复写
-
子类继承父类的成员属性和成员方法后,如果对其“不满意”,那么可以进行复写。
-
即:在子类中重新定义同名的属性或方法即可。
-
一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员
-
如果需要使用被复写的父类的成员,需要特殊的调用方式
📄 方式1:调用父类成员
🐤 使用成员变量:父类名.成员变量
🐤 使用成员方法:父类名.成员方法(self)
📄 方式2:使用super()调用父类成员
:baby_chick: 使用成员变量:super().成员变量 :baby_chick: 使用成员方法:super().成员方法()
class Phone: producer = 'XZQ' IMEI = None def call_by_5g(self): print('5G通话') class MyPhone(Phone): producer = 'HM' # 复写父类的成员属性 # 复写父类的成员属性 def call_by_5g(self): Phone.call_by_5g(self) # 方式一 super().call_by_5g() # 方式二 print(super().producer) print(Phone.producer) print('单核省电5G') phone = MyPhone() phone.call_by_5g() print(phone.producer) # 5G通话 # 5G通话 # XZQ # XZQ # 单核省电5G # HM
-
-
-
多态
-
理解
- 多态,指的是:多种状态,即完成某个行为时,使用不同的对象会得到不同的状态。
- 同样的行为(函数),传入不同的对象,得到不同的状态
- 多态常作用在继承关系上.
-
案例:
- 函数(方法)形参声明接收父类对象
- 实际传入父类的子类对象进行工作
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.speak() dog = Dog() cat = Cat() # 以子类做实际工作 make_noise(dog) # 汪汪汪 make_noise(cat) # 喵喵喵
-
抽象类(接口)
父类用来确定有哪些方法,具体的方法实现,由子类自行决定,这种写法,就叫做抽象类(也可以称之为接口)
-
抽象类:含有抽象方法的类称之为抽象类
-
抽象方法:**方法体是空实现的(pass)**称之为抽象方法
-
案例:
# 空调抽象类 class AC: def cool_wind(self): pass def hot_wind(self): pass def swing_lr(self): pass class Midea_AC(AC): def cool_wind(self): print('美的核心制冷科技') def hot_wind(self): print('美的核心制热科技') def swing_lr(self): print('美的无感左右摆风') class GREE_AC(AC): def cool_wind(self): print('格力核心制冷科技') def hot_wind(self): print('格力核心制热科技') def swing_lr(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) # 格力核心制冷科技
-
-