面向对象的三大特性
面向对象编程(OOP)有三大核心特性,分别为封装、继承、多态,共同解决软件开发中复杂性管理、代码复用、灵活性扩展。封装主要是隐藏复杂性,保护数据安全,继承主要是子承父业,实现代码的复用,构建层次化结构。多态主要是统一接口,方便扩展。接下来将从三大特性分别入手详细介绍一下三大特性在代码中的应用。
封装
什么是封装
封装就是对外隐藏类内部的具体实现,仅对外提供公共的访问方法
为什么需要封装
封装的应用可以隐藏类内部的实现细节,保证代码的隐蔽性,提高代码的安全性。
封装的具体应用
封装就是将属性和行为写在类中,其中可以通过在属性和方法的开头添加双下划线将属性和方法私有,私有化的属性和方法只能在类的内部调用。如果想在类外部调用私有化的属性,只能在类的内部添加公共的访问方法,如get__xxx()方法可以让类外部获取私有化属性,而set__xxx()方法为类外部提供修改私有属性的公共访问方法。
pythong
class Person(object):
# 初始化属性
def __init__(self,name):
"""
初始化属性
:param name:
"""
self.name = name
self.__money = 1000
# 设置公共访问接口,获取私有属性
def get_money(self):
return self.__money
# 设置公共的访问接口,设置私有属性
def set_money(self,money):
if self.__validate_money(money):
self.__money += money
def __validate_money(self,money):
"""
验证交易的合法性
:param money: 待验证的金额
:return: 布尔值,表示交易是否合法
"""
if not isinstance(money,float) or money < 0:
return False
if __name__ == '__main__':
p = Person("小明")
print(p.name)
# 私有属性,不能被直接访问
# print(p.money)
# 调用公共访问方法查看,私有属性
print(p.get_money())
# 调用公共访问访问设置私有属性
p.set_money(1)
print(p.get_money())
继承
什么是继承
继承顾名思义就是子承父业,子类能继承父类的属性和方法。继承的好处在与能简化代码的书写,提高代码的复用性。
为什么需要继承
如果多个类中具有相同的属性和方法,为的减少代码重发,可以将重复的代码抽取出来新创建一个类,然后其他类继承该类,就实现的代码复用,同样减少的维护成本。
继承的具体应用
继承的格式如下:
python
class 子类名(父类名):
pass
# 例如:
class B(A):
pass
# 其中类B可以叫做子类、派生类、扩展类
# 类A称为父类、基类、超类
python
案例:定义父类Father,属性:性别男,行为:散步,子类Son,继承自父类,创建对象并测试
# 父类
class Father(object):
def __init__(self):
self.sex = "男"
def walk(self):
print("散散步,有利于健康")
# 子类,继承自Father
class Son(Father):
pass
# 只在本文件中运行
if __name__ == '__main__':
son = Son()
son.walk()
print(son.sex)
单继承
单继承指一个子类只继承一个父类,这个类会具有父类的属性和方法
python
# 案例:定义一个人类,属性:姓名,年龄,行为:学习。定义一个老师类,一个学生类,需要继承自人类
# 定义人类
class Person(object):
# 初始化方法,创建对象时自动调用
def __init__(self, name, age):
self.name = name
self.age = age
# 学习的方法(行为)
def study(self):
print(f"{self.name}活到老,学到老......")
class Teacher(Person):
pass
class Student(Person):
pass
if __name__ == '__main__':
# 创建对象
t1 = Teacher("张三", 28)
s1 = Student("李四", 18)
t1.study()
print("-"*10)
s1.study()
多继承
多继承指一个子类继承多个父类。这个类会同时具有父类的属性和方法。多继承一般都会存在一个菱形继承问题,python中解决的这个问题靠的是方法解析顺序(Method Resolution Order,MRO)算法解决这个问题。 那什么是菱形问题呢? 假设我们有以下类结构:
python
A
/ \
B C
\ /
D
类B和类C都继承自类A,类D同时继承自类B和C,这样类D会通过两条路径继承到类A的属性和方法,形成"菱形"结构。 Python 使用 C3 线性化算法 (C3 Linearization)来确定方法的解析顺序(MRO),确保每个类在继承链中只出现一次,并且遵循以下原则: 子类优先于父类 从左到右的继承顺序 保持继承关系的一致性 MRO 的作用
- 避免重复调用:通过 C3 算法生成唯一的调用顺序,确保每个类的方法最多执行一次。
- 确定调用优先级:明确哪个父类的方法先被查找和执行。
- 支持
super()
正确工作 :super()
不是简单地调用"父类",而是根据 MRO 调用"下一个类"。 注意事项 - 必须使用
super()
:为了使 MRO 正常工作,建议在重写方法时始终使用super()
调用父类方法。如果直接调用A.method(self)
,会绕过 MRO,可能导致重复执行。 - 避免不一致的继承顺序:虽然 Python 允许多重继承,但设计时应尽量避免过于复杂的继承结构,以提高可读性和可维护性。
python
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def teach(self):
print("原始人很好学")
class Person1(object):
def __init__(self, name, age):
self.name = name
self.age = age
def teach(self):
print("原始人很好学")
class F(Person):
def teach(self):
super().teach()
print(f"{self.name}Father线上")
class M(Person):
def teach(self):
super().teach()
print(f"{self.name}Mother线下")
class Child(F, M):
def teach(self):
# Person.teach(self)
super().teach()
print("-----------------")
F.teach(self)
# print("-----------------")
print(f"{self.name}Child两者结合")
if __name__ == '__main__':
c = Child("张三",18)
c.teach()
# 打印方法调用顺序
# print(Child.__mro__)
print(Child.mro())
多态
什么是多态
多态指在同一方法在不同的场景下有不同的形态,python中的多态指同一个函数,传入不同的对象有不同的结果返回,多态的实现有三个条件,分别为继承、重写、父类引用指向子类对象。python中的多态是一个伪多态,只是模仿多态的功能因为python是一门动态语言(动态语言(运行时决定一切)是一种在程序运行时才进行类型检查、方法绑定、结构定义等操作的编程语言,不需要声明变量类型,动态语言更加灵活,在运行时更具有可变性。与之相反的就是静态语言是在编译时检查类型,同时需要声明变量类型),在调用实例方法时,不检查类型,只有方法存在,参数正确,就可以运行,无类型约束,只要参数符合就能调,不管是不是他的子类。行为上的多态,具有多态的功能。
为什么需要多态
不同对象对同一操作可能有不同的操作,如何让程序动态适应这些差异?最好的解决方法就是使用多态,实现"一个接口,多种实现"。
多态的具体应用
父类型当做函数参数,可以接收任意的子类对象,提升代码的扩展性,解耦合。
python
class Animal(object):
def speak(self):
print("动物叫")
class Dog(Animal):
def speak(self):
print("旺旺哇")
class Cat(Animal):
def speak(self):
print("喵喵叫")
class Person(object):
def speak(self):
print("哇哇叫")
def make_noise(animal: Animal):
animal.speak()
if __name__ == '__main__':
make_noise(Animal())
make_noise(Cat())
make_noise(Dog())
# python是一种动态语言,动态语言调用方法时,不检查类型,只有方法存在,参数正确,就可以调用
# 这个就是伪多态
make_noise(Person())
面向对象的其他特性
实例属性(对象属性)
实例属性是指每个对象特有的属性,一个对象中实例对象实例属性的修改不会影响到其他实例对象中的实例属性。
类属性
类属性是类的属性,这个类下所有对象所共享的属性,任何的修改其他对象都能看到。
在Python中,一切皆对象。类也是一个特殊的对象,我们可以单独为类定义属性。
python
# 需求:定义学生类,有name属性,teacher_name属性,创建学生类对象,并测试
class Student(object):
teacher_name = "张三"
def __init__(self, name):
self.name = name
if __name__ == '__main__':
s1 = Student("小四")
s2 = Student("小五")
print(s1.name)
print(s1.teacher_name)
print(s2.name)
print(s2.teacher_name)
# 换老师
print("-"*20)
Student.teacher_name = "李四"
print(s1.name)
print(s1.teacher_name)
print(s2.name)
print(s2.teacher_name)
类方法
为什么需要类方法,在面向对象中,特别强调数据封装性。所以不建议直接在类的外部对类属性进行直接获取。所以我们如果想操作类属性,建议使用类方法。
静态方法
在开发时,如果需要在类中封装一个方法,这个方法: 1、既 不需要访问实例属性或者调用实例方法 2、也 不需要访问类属性或者调用类方法 这个时候,可以把这个方法封装成一个静态方法
python
class Student(object):
teacher_name = "张三"
def __init__(self, name):
self.name = name
# 定义类方法,访问:teacher_name这个类变量
@classmethod
def methes1(cls): #cls是class的意思
print(cls.teacher_name) # 相当于类名.类属性名的方式访问类变量
# 定义静态方法,访问:teacher_name这个类变量
@staticmethod
def methes2():
print(Student.teacher_name)
if __name__ == '__main__':
s1 = Student("小李")
Student.methes1()
s1.methes1()
Student.methes2()