面向对象详解,面向对象的三大特征:封装、继承、多态-1
https://developer.aliyun.com/article/1538240
三、面向对象的三大特征:封装、继承、多态
1. 封装
封装(Encapsulation):将数据和操作封装在对象中,使其成为一个独立的实体,外界只能通过对象提供的接口访问和操作内部数据。
对用户隐藏的属性和行为
现实世界中的事物,有属性和行为。但是不代表这些属性和行为都是开放给用户使用的。
私有成员
既然现实事物有不公开的属性和行为,那么作为现实事物在程序中映射的类,也应该支持。
类中提供了私有成员的形式来支持。
- 私有成员变量
- 私有成员方法
定义私有成员的方式非常简单,只需要:
- 私有成员变量:变量名以__开头(2个下划线)
- 私有成员方法:方法名以__开头(2个下划线)
class Phone: IMEI = None # 序列号 producer = None # 厂商 # 私有成员变量 __current_voltage = None # 当前电压 def call_by_5g(self): print('5g通话已开启') # 私有成员方法 def __keep_5g(self): print('保持5g')
私有方法无法直接被类对象使用
phone = Phone() phone.__keep_5g()
使用私有成员
class Phone: # 构造方法 def __init__(self, __is_5g_enable): # 设置私有变量 self.__is_5g_enable = __is_5g_enable # 设置私有方法 def __check_5g(self): if self.__is_5g_enable == True: # 类内部访问私有成员要用self print('5G开启') else: print('5G关闭,使用4G网络') def call_by_5g(self): self.__check_5g() print('正在通话中...') phone = Phone(False) phone.call_by_5g()
2. 继承
继承(Inheritance):继承允许一个类(子类)继承另一个类(父类)的属性和方法,子类可以重用父类的代码,并且可以在不修改原有代码的情况下进行扩展和修改。
简单来说就是一个类,继承另外一个类的成员变量和成员方法
举个简单的例子:
手机的更新换代并不是完完全全重新开始,而是在原先的基础上新添一些属性和功能。
类比到程序中,我们也可以在已经设计好的类上进行更新和修改,这就会用到继承。
继承分为:单继承和多继承
单继承语法:
class 类名(父类): 类内容体
# 单继承演示 # 定义父类 class Phone: IMEI = None # 序列号 producer = 'APPLE' # 厂商 def call_by_4g(self): print('4g通话') # 定义子类 class Phone2022(Phone): face_id = '10001' def call_by_5g(self): print('5g通话') phone = Phone2022() phone.call_by_4g() # 输出:4g通话 print(phone.producer) # 输出:APPLE phone.call_by_5g() # 输出:5g通话 print(phone.face_id) # 输出:10001
通过继承可以将从父类那里继承(复制)来成员变量和成员方法(不含私有)给子类使用。
多继承语法
Python的类之间也支持多继承,即一个类,可以继承多个父类
class 类名(父类1, 父类2, 父类3, ... , 父类N): 类内容体
举例:
# 多继承演示 # 定义父类 class Phone: IMEI = None # 序列号 producer = 'HM' # 厂商 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 MyPhone(Phone, NFCReader, RemoteControl): pass phone = MyPhone() print(phone.producer) # 输出:HM print(phone.nfc_type) # 输出:第五代 phone.read_card() # 输出:读取NFC卡 phone.call_by_4g() # 输出:4g通话 phone.control() # 输出:红外遥控开启
多继承注意事项:多个父类中,如果有同名的成员,那么**默认以继承顺序(从左到右)**为优先级。即:先继承的保留,后继承的被覆盖。
pass语句
pass是占位语句,用来保证函数(方法)或类定义的完整性,表示无内容,空的意思。
复写
子类继承父类的成员属性和成员方法后,如果对其“不满意”,那么可以进行复写。
即:在子类中重新定义同名的属性或方法即可。
# 定义父类 class Phone: IMEI = None # 序列号 producer = 'APPLE' # 厂商 def call_by_4g(self): print('4g通话') # 定义子类 class MyPhone(Phone): producer = 'HUAWEI' # 复写父类属性 def call_by_4g(self): # 复写父类方法 print('可用5g通话') phone = MyPhone() print(phone.producer) # 输出:HUAWEI phone.call_by_4g() # 输出:可用5g通话
调用父类同名成员
一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员
如果需要使用被复写的父类的成员,需要特殊的调用方式:
方式一:
- 调用父类成员
使用成员变量:父类名.成员变量
使用成员方法:父类名.成员方法(self)
方式二:
- 使用super()调用父类成员
使用成员变量:super().成员变量
使用成员方法:super().成员方法()
注意:只能在子类内调用父类的同名成员。子类的类对象直接调用会调用子类复写的成员
# 定义父类 class Phone: IMEI = None # 序列号 producer = 'APPLE' # 厂商 def call_by_4g(self): print('4g通话') # 定义子类 class MyPhone(Phone): producer = 'HUAWEI' # 复写父类属性 def call_by_4g(self): # 复写父类方法 print('可用5g通话') # 调用父类属性和方法 # 方法一 # print(f'调用父类属性[1]:{Phone.producer}') # 调用父类属性 # Phone.call_by_4g(self) # 调用父类方法 # 方法二 print(f'调用父类属性[2]:{super().producer}') # 调用父类属性 super().call_by_4g() # 调用父类方法 phone = MyPhone() print(phone.producer) phone.call_by_4g()
3. 多态
多态(Polymorphism):多态使得对象可以根据上下文表现出不同的行为,同一个方法可以在不同对象上有不同的实现。这样可以提高代码的灵活性和可复用性。
也就是说:多种状态,即完成某个行为时,使用不同的对象会得到不同的状态。
如何理解?
同样的行为(函数),传入不同的对象,得到不同的状态
多态常作用在继承关系上。
比如
- 函数(方法)形参声明接收父类对象
- 实际传入父类的子类对象进行工作
即:
- 以父类做定义声明
- 以子类做实际工作
- 用以获得同一行为, 不同状态
抽象类(接口)
抽象类:含有抽象方法的类称之为抽象类
抽象方法:方法体是空实现的(pass) 称之为抽象方法
举例:
# 抽象类 # 定义父类 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 cool_wind(ac: AC): ac.cool_wind() # 创建对象 midea = Midea_AC() gree = GREE_AC() cool_wind(midea) # 输出:美的空调制冷 cool_wind(gree) # 输出:格力空调制冷
四、类型注解
在PyCharm中编写代码,我们经常能够见到如下提示:
为什么PyCharm工具能够做到这一点?
因为:PyCharm确定这个对象,是list类型
同样,我们换一份代码:定义一个函数func,接收一个参数data,你会发现,PyCharm不会在做出任何提示了
为什么PyCharm工具无法提示了?
这是因为PyCharm不确定这个对象是什么类型
PyCharm无法通过代码确定应传入什么类型,我们需要使用类型注解
1. 变量的类型注解
基础语法: 变量: 类型
为变量设置注解,显示的变量定义,一般无需注解
# 变量的类型注 var1: int = 10 var2: str = '张三' var3: bool = True
像上面这样:就算不写注解,也明确的知晓变量的类型
2. 基础容器类型注解
# 基础容器类型注解 my_list: list = [1, 2, 3] my_tuple: tuple = (1, 2, 3) my_dict: dict = {"name": "张三"} # 容器类型详细注解 my_list1: list[int] = [1, 2, 3] my_tuple1: tuple[int, str, bool] = (1, "张三", True) my_dict1: dict[str, str] = {"name": "张三"}
注意:
- 元组类型设置类型详细注解,需要将每一个元素都标记出来
- 字典类型设置类型详细注解,需要2个类型,第一个是key第二个是value
3. 类对象类型注解
# 类对象类型注解 class Student: pass stu: Student = Student()
4. 函数形参和返回值的类型注解
函数和方法的形参类型注解语法:
def 函数名(形参1: 类型, 形参2: 类型, ..., 形参n: 类型): pass
# 函数形参的类型注解 def add(x: int, y: int): return x + y
返回值注解
语法:
def 函数名(形参1: 类型, 形参2: 类型, ..., 形参n: 类型) -> 返回值类型: pass
# 函数形参和返回值的类型注解 def fnuc(data: list) -> list: return data
除了使用 变量: 类型, 这种语法做注解外,也可以在注释中进行类型注解。
语法:# type: 类型
# 在注释中进行类型注解 var_1: random.randint(1, 10) # type: int var_2: json.loads('{"name": "张三"}') # type: dict
5. Union类型联合类型注解
Union 类型用于指定一个变量可以是多种类型中的一种。
Union联合类型注解,在变量注解、函数(方法)形参和返回值注解中,均可使用。
Union的使用方式
导包:from typing import Union
语法:Union[类型, …, 类型]
# 使用Union类型,必须先导包 from typing import Union my_list: list[Union[int, str, dict]] = [1, 2, 'name', {"age": 18}] my_dict: dict[str, Union[int, str, list]] = {"name": "张三", "age": 18, "grade": [98, 97]} my_list1: list[Union[int, str, dict[Union[str, int]]]] = [1, 2, 'name', {"age": 18}] # 函数的Union类型 def func(data: Union[int, str]) -> Union[int, str]: return data