【Python】面对对象超全总结:封装,继承,多态
文章目录
- 一.类与对象
- 二.类中的特殊方法
- 2.1构造方法
- 2.2魔术方法
- 三.封装
- 四.继承
- 4.1单继承
- 4.2多继承
- 4.3重写
- 五.运算符重载
- 六.类型注解
- 6.1变量的类型注解
- 6.2函数(方法)的类型注解
- 6.3Union类型
- 七.多态
- 八.抽象类(接口)
我们先来看一个使用对象组织数据:
【代码示例】:
class Student:
name = None # 记录学生的姓名
gender = None # 记录学生的性别
nationality = None # 记录学生国籍
native_place = None # 记录学生籍贯
age = None
# 创建一个对象
stu_1 = Student
# 对象属性进行赋值
stu_1.name = "林俊杰"
stu_1.gender = "男"
stu_1.nationality = "中国"
stu_1.native_place = "山东省"
stu_1.age = 31
# 获取对象中的信息
print(stu_1.name)
print(stu_1.gender)
print(stu_1.nationality)
print(stu_1.native_place)
print(stu_1.age)
'''运行结果:
林俊杰
男
中国
山东省
31'''
一.类与对象
类的定义
python中使用class关键字来定义一个类,语法如下:
class 类名:
属性名 = 属性值
def 方法名(self,形参1,...,形参n):
方法体
在类中可以定义对象的属性和方法,属性和方法类似于前面所讲的变量和函数,但类中方法形参表的第一个参数是self,它是一个指代对象的默认参数。
- 它用来表示类对象自身的意思
- 当我们使用类对象调用方法时,self会自动被Python传入
- 在方法内部,想要访问类的成员变量,必须使用self
即在成员方法中访问成员变量,是通过参数列表中self.
访问到的
【代码示例】
class Student:
name = None
def say_hi(self):
print(f"大家好,我是{self.name},欢迎大家")
stu = Student()
stu.name = "菜菜"
stu.say_hi()
'''运行结果:
大家好,我是菜菜,欢迎大家'''
二.类中的特殊方法
2.1构造方法
作用:在创建对象时对对象的属性进行初始化。
- 在创建类对象(构造类)的时候,会自动执行
- 在创建对象(构造类)的时候,将传入参数自动传递给
_init_
方法使用
每个类中都有一个构造方法__init__()
,如果类中显式定义了构造方法,那么创建对象时调用的是显式定义的构造方法;否则调用默认的构造方法。
init()方法可以分为无参构造方法和有参构造方法
(1)使用无参构造方法创建对象时,所有对象的属性都有相同的初始值
(2)使用有参构造方法创建对象时,所有对象的属性可以有不同的初始值
【代码示例】
class Student:
def __init__(self,name,age,tel):
self.name = name # 赋值的同时定义了成员变量
self.age = age
self.tel = tel
print("Student类创建了一个类对象")
stu = Student("菜菜",22,"1237867826")
print(stu.name)
print(stu.age)
print(stu.tel)
'''运行结果:
Student类创建了一个类对象
菜菜
22
1237867826'''
2.2魔术方法
上面学习的__init__
构造方法,是Python类内置的方法之一。这些内置的类方法,各有各的特殊功能,这些内置方法我们称之为——魔术方法
下面是几个常见的魔术方法:
__str__
字符串方法
当类对象需要被转换成字符串之时,输出的结果是内存地址,内存地址没有多大作用,我们可以通过__str__
方法,控制类转换为字符串的行为。
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}"
stu = Student("菜菜",22)
print(stu)
print(str(stu))
'''运行结果:
Student类对象,name:菜菜,age:22
Student类对象,name:菜菜,age:22'''
__lt__
大于小于比较方法
直接对两个对象进行比较是不可以的,但是在类中实现__lt__
方法,即可同时完成:小于符号和大于符号两种比较
class Student:
def __init__(self,name,age):
self.name = name
self.age = age
# __lt__魔术方法
def __lt__(self,other):
return self.age < other.age
stu1 = Student("菜菜",22)
stu2 = Student("肉肉",24)
print(stu1 < stu2)
print(stu1 > stu2)
'''运行结果:
True
False'''
__le__
小于等于比较方法
__le__
可用于:<=,>=两种比较运算符上
class Student:
def __init__(self,name,age):
self.name = name
self.age = age
# __lt__魔术方法
def __lt__(self,other):
return self.age < other.age
# __le__魔术方法
def __le__(self,other):
return self.age <= other.age
stu1 = Student("菜菜",22)
stu2 = Student("肉肉",22)
print(stu1 <= stu2)
print(stu1 >= stu2)
'''运行结果:
True
True'''
__eq__
比较运算符实现方法
- 不实现
__eq__
方法,对象之间可以比较,但是比较内存地址,也就是:不同对象==比较一定是False结果 - 实现了
__eq__
方法,就可以按照自己的想法来决定两个对象是否相等了
class Student:
def __init__(self,name,age):
self.name = name
self.age = age
# __eq__魔术方法
def __eq__(self,other):
return self.age <= other.age
stu1 = Student("菜菜",22)
stu2 = Student("肉肉",22)
print(stu1 == stu2)
'''运行结果:
True'''
三.封装
封装是面向对象的重要特征之一,它的基本思想是对外隐藏类的实现细节,提供用于访问类成员的公开接口。
为了契合封装的思想,定义类时要满足以下两点:
- 将属性私有化
- 定义两个供外界调用的公有方法,分别用于设置和获取私有属性的值。
定义私有成员:
- 私有成员变量:变量名以__开头(两个下划线)
- 私有成员方法:方法名以__开头(两个下划线)
使用私有成员:
私有成员无法被类对象使用,但是可以被其他成员使用
【代码示例】
class Phone:
__current_voltage = 0.5
def __keep_single_core(self):
print("让CPU以单核模式运行")
def call_by_5g(self):
if self.__current_voltage >= 1:
print("5g通话已开启")
else:
self.__keep_single_core()
print("电量不足")
phone = Phone()
phone.call_by_5g()
'''运行结果:
让CPU以单核模式运行
电量不足
'''
四.继承
继承是面向对象的重要特征之一,它主要用于描述类与类之间的关系,在原有类的基础上扩展原有类的功能。如果类与类之间具有继承关系,被继承的类称为父类或基类,继承其他类的类称为子类或派生类,子类会自动拥有父类的公有成员,但不会拥有父类的私有成员,也不能访问父类的私有成员。
4.1单继承
单继承就是子类只继承一个父类,其语法格式为:
class 子类名(父类名):
【代码示例】
class Phone:
IMEI = None
producer = "HM"
def call_by_4g(self):
print("4g通话")
class Phone2022(Phone):
face_id = "10001"
def call_by_5g(self):
print("2022年新功能:5g通话")
phone = Phone2022()
print(phone.producer)
phone.call_by_4g()
phone.call_by_5g()
'''运行结果:
HM
4g通话
2022年新功能:5g通话'''
4.2多继承
程序中的一个类可以继承多个类,也自动拥有所有父类的公有成员,多继承的语法格式为:
class 子类名(父类名1,父类名2,...)
如果继承的多个父类中有同名的方法怎么办?
如果子类继承的多个父类是平行关系的类,那么子类先继承哪个类,就会先调用哪个类的方法。
pass关键字
是普通的占位语句,用来保证函数或者方法或者类定义法的完整性
【代码示例】
# 父类1
class Phone:
IMEI = None
producer = "HM"
def call_by_4g(self):
print("4g通话")
class Phone2022(Phone):
face_id = "10001"
def call_by_5g(self):
print("2022年新功能:5g通话")
# 父类2
class NFCReader:
nfc_type = "第五代"
producer = "HN"
def read_card(self):
print("NFC读卡")
def write_card(self):
print("NFC写卡")
# 父类3
class RemoteControl:
rc_type = "红外遥控"
def control(self):
print("红外遥控开启")
# 子类
class MyPhone(Phone,NFCReader,RemoteControl):
pass # 补全语法,不让语法产生错误
phone = MyPhone()
print(phone.producer)
phone.call_by_4g()
phone.read_card()
phone.write_card()
phone.control()
'''运行结果:
HM
4g通话
NFC读卡
NFC写卡
红外遥控开启'''
4.3重写
子类会原封不同地继承父类的方法,但子类有时需要按自己的需求对继承来的方法进行调整。
此时可以在子类中重写从父类继承来的方法。
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("关闭5g")
phone = MyPhone()
phone.call_by_5g()
print(phone.producer)
'''运行结果:
关闭5g
ITHEIMA'''
多用父类同名成员
子类重写了从父类继承来的方法后,无法直接访问父类的同名方法,
如果需要使用被重写的父类的话,需要特殊的调用方式:
①调用父类成员
使用成员变量:父亲名.成员变量
使用成员方法:父亲名.成员方法(self)
②使用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):
# 方式1
# print(f"父类的产商是:{Phone.producer}")
# Phone.call_by_5g(self)
# 方式2
print(f"父类的产商是:{super().producer}")
super().call_by_5g()
print("关闭5g")
phone = MyPhone()
phone.call_by_5g()
print(phone.producer)
'''运行结果:
父类的产商是:ITCAST
5g通话
关闭5g
ITHEIMA'''
六.类型注解
在代码中涉及数据交互的地方,提供数据类型的注解(显式的说明)
主要功能:
- 帮助第三方IDE工具(如PyCharm)对代码进行类型推断,协助做代码提示
- 帮助开发者自身对变量进行类型注释
支持:
- 变量的类型注解
- 函数(方法)形参列表和返回值的类型注解
6.1变量的类型注解
【语法】:变量:类型
除了使用上述方法做注解外,也可以在注释中进行类型注解
【语法】:# type:类型
注意:类型注解仅仅是提示性的,不是决定性的。数据类型和注解类型也不会导致错误。
6.2函数(方法)的类型注解
【语法】:
def 函数方法名(形参名:类型,形参名:类型,....):
pass
对返回值进行注解:
def 函数方法名(形参名:类型,形参名:类型,....)->返回值类型:
pass
6.3Union类型
使用Union可以定义联合类型注解
Union的使用方式:
- 导包:
from typing import Union
- 使用:
Union[类型,....,类型]
同样,union也可以给函数做注解->
七.多态
多态是面向对象的重要特征,它的表现形式是:让不同类的同一方法,可以通过相同的接口调用,表现出不同的行为。
例如:定义一个Cat类和一个Dog类,两个类中都定义shout()方法:
class Cat:
def shout(self):
print("喵喵")
class Dog:
def shout(self):
print("汪汪")
#定义一个接口,通过这个接口调用Cat类和Dog类中的shout()方法
def shout(obj):
obj.shout()
cat = Cat()
dog = Dog()
shout(cat)
shout(dog)
在函数shout(obj)中,参数obj代表一个对象,可以是Cat类的实例,也可以是Dog类的实例,甚至是其他类的实例。通过传递不同的对象给shout(obj)函数,可以实现对不同类中相同名称方法的调用。
利用多态这一特性编写代码不会影响类的内部设计,但可以提高代码的兼容性,让代码的调度更加灵活。
可见,父类Animal的shout方法,是空实现:
八.抽象类(接口)
抽象类就好比定义一个标准
包含了一些抽象的方法,要求子类必须实现。自己没有具体的实现方法。