本文目录
面向对象–三大特性
- 封装 根据职责将 属性 和 方法 封装到一个抽象 类 中
- 继承 实现代码的重用(复用),实现相同的代码不需要重复的编写
- 多态 不同的子类对象调用相同的父类方法,产生不同的执行结果,增加代码灵敏度
面向对象–继承
继承,让类和类之间有上下级联动,为稍后的多态做好准备
class Animal:
"""
@ Author: EvanZhang
@ Date: 2025/4/7 14:47
@ Info: 动物主类
"""
def __init__(self):
pass
def eat(self):
print("1")
def sleep(self):
print("2")
def play(self):
print("3")
class Cat(Animal):
class Dog(Animal):
tom = Cat()
tom.play()
spk = Dog()
spk.sleep()
# 结果
3
2
继承的语法
class 父类():
pass
class 子类(父类名): # 继承,在类的括号中直接引入父类
pass
- 子类继承自父类,拥有父类的所有方法和属性,可以直接调用父类中已经封装好的方法,不需要二次开发
- 子类中可以根据自身的需要,封装属于自己的属性和方法
专业术语
在上面的例子中:
- Dog 类是 Animal 类的子类,Animal 类是 Dog 类的父类,Dog 类从 Animal 类 继承
- Dog 类是 Animal 类的派生类,Animal 类是 Dog 类的基类,Dog 类从 Animal 类派生
继承的传承性
如果C类从B类继承,B类又从A类继承,那么C类也拥有A类的属性和方法
即 子类拥有父类及父类的父类中封装的所有属性和方法
父类与子类关系
子类父类内存空间示意
方法重写
- 当父类的方法实现不能满足子类需求时,可以对方法进行 重写(override)
重写 父类方法有两种情况:
-
覆盖 父类的方法
-
对父类方法进行扩展
覆盖父类的方法
- 如果在开发中,父类的方法实现 和 子类的方法实现,完全不同
- 就可以使用 覆盖 的方式,在子类中 重新编写 父类的方法实现
具体的实现方式,就相当于在 子类中 定义了一个 和父类同名的方法并且实现
重写之后,在运行时,只会调用 子类中重写的方法,而不再会调用 父类封装的方法
示例代码:
class Animal:
def __init__(self):
pass
def eat(self):
print("eat")
class Cat(Animal):
def eat(self):
print("鱼")
class Rabbit(Animal):
def eat(self):
print("草")
tom = Cat()
tom.eat()
mltt = Rabbit()
mltt.eat()
# 结果
鱼
草
对父类方法进行扩展
标准方法
如果在开发中,子类的方法实现中 包含父类的方法实现,即父类原本封装的方法实现 是 子类方法的一部分,就可以使用扩展的方式对父类方法进行扩展
- 在子类中 重写 父类的方法
- 在需要的位置使用
super().父类方法
来调用父类方法的执行 - 代码其他的位置针对子类的需求,编写 子类特有的代码实现
关于
super
- 在
Python
中super
是一个 特殊的类super()
就是使用super
类创建出来的对象- 最常 使用的场景就是在 重写父类方法时,调用 在父类中封装的方法实现
调用父类方法的另外一种方式(知道)
在 Python 2.x
时,如果需要调用父类的方法,还可以使用以下方式:
父类名.方法(self)
- 这种方式,目前在
Python 3.x
还支持这种方式 - 这种方法 不推荐使用,因为一旦 父类发生变化,方法调用位置的 类名 同样需要修改
提示
- 在开发时,
父类名
和super()
两种方式不要混用- 如果使用 当前子类名 调用方法,会形成递归调用,出现死循环
子类调用父类的属性
调用父类中的方法或属性
子类可以通过super()
来调用父类的方法或属性
class User:
def __init__(self,name,age):
self.name = name
self.age = age
class Studend(User):
def __init__(self,name,age,id)
super().__init__(name,age)
self.id = id # 调用父类属性
调用自身类的方法或属性
通过 类.方法/变量 来调用
class A:
a = 1
__b = 2
def __init__(self):
print(A.a,A.__b) # 通过 类.方法/变量 来调用
pass
多继承
子类 可以拥有 多个父类,并且具有 所有父类 的 属性 和 方法
class User:
def add(self):
print("v1")
class Person:
def add(self):
print("v2")
def add2(self):
print("v2")
class Student(User,Person): # 多继承,第一个User,第二个Person
pass
lufei = Student()
lufei.add()
lufei.add()
lufei.add2()
# 结果
v1
v1
v2
当多继承中有某两个父类的方法名相同时,会优先使用子类中第一个继承的父类的方法,包括重写也是
因此,尽量避免在多继承的父类中使用相同的用户名,如果实在避不开,则考虑避免使用多继承,避免产生混淆!
新式类与旧式(经典)类
object
是Python
为所有对象提供的 基类,提供有一些内置的属性和方法,可以使用dir
函数查看
- 新式类:以
object
为基类的类,推荐使用 - 经典类:不以
object
为基类的类,不推荐使用 - 在
Python 3.x
中定义类时,如果没有指定父类,会 默认使用object
作为该类的 基类 ——Python 3.x
中定义的类都是 新式类 - 在
Python 2.x
中定义类时,如果没有指定父类,则不会以object
作为 基类
新式类 和 经典类 在多继承时 —— 会影响到方法的搜索顺序
为了保证编写的代码能够同时在 Python 2.x
和 Python 3.x
运行!
今后在定义类时,如果没有父类,建议统一继承自 object
class 类名(object):
pass
继承中子类父类初始化问题
在子类没有提供init初始化方法时,子类在初始化的过程中时会默认先初始化父类
如果程序员在子类手动提供了init的初始化方法,那么在执行过程中Python解释器不会去执行父类的init,此时如果要使用父类的初始化属性则会报错
解决方法:在子类初始化时手动使用super().__init__
调用父类初始化方法
虽然在子类手动提供init会使父类的init不执行,但仍会执行父类的new方法去开辟内存空间
抽象类
抽象类引入
主旨思想:在后续的开发过程中需要严格统一开发标准,标准化开发,约束子类开发,强制所有子类重写父类的抽象方法
# 场景 支付:微信、支付宝、云闪付、银行...
# 统一支付接口
class WeChat(object):
"""
@Author: EvanZhang
@Date: 2025/4/8 11:09
@Info: 微信支付接口
"""
def __init__(self):
pass
def pay(self,money):
print("wechat pay %.2f"%money)
class Ali(object):
"""
@Author: EvanZhang
@Date: 2025/4/8 11:09
@Info: 阿里支付接口
"""
def __init__(self):
pass
def pay(self,money):
print("ali pay %.2f"%money)
# wechat = WeChat()
# wechat.pay(100)
# ali = Ali()
# ali.pay(200)
# 对代码进行改造:统一支付
def pay(obj,money):
obj.pay(money)
wechat = WeChat()
ali = Ali()
pay(wechat,150)
pay(ali,200)
上述代码问题:虽然开发了pay()统一支付方式,但是程序员(用户)仍然可以使用自己的支付方式(24-27行代码)来执行
因此接下来需要限定用户必须通过指定方式来执行代码,那么就需要用到抽象类技术
定义抽象类
第一步,引入抽象类模块
from abc import abstractmethod,ABCMeta #引入抽象方法和抽象类
第二步,在需要成为抽象类的类名后面添加抽象类模块
class BaseDao(metaclass=ABCMeta):
第三步,在抽象类方法前加入@abstractmethod
注解
@abstractmethod
def addUser(self):
pass
第四步,子类继承抽象类,强制实现抽象类中的方法
# 场景:数据库的增删改查
class UserDao(BaseDao):
def addUser(self):
print("对接数据库做增加操作")
pass
def updateUser(self):
print("对接数据库做更新操作")
pass
def delUser(self):
print("对接数据库做删除操作")
pass
def getUser(self):
print("对接数据库做获取操作")
pass
面向对象–多态
定义
一种对象存在多种形态,称为多态,即 不同的子类对象调用相同的父类方法,产生不同的执行结果
- 增加代码灵敏度
- 以继承和重写父类方法为前提
- 是调用方法的技巧,不影响类的内部设计
案例
地上有一群鸟(20只),一声枪响,有的飞起来了,有的跑起来了…
class Bird:
def run(self):
print("飞或者跑....")
class FlyBird(Bird):
def run(self):
print("飞起来了....")
class RunBird(Bird):
def run(self):
print("跑起来了.........")
class Person:
def play(self,bird):
bird.run()
import random
lufei = Person()
for i in range(20):
num = random.randint(0,1)
if num == 0:
bird = FlyBird()
elif num == 1:
bird = RunBird()
lufei.play(bird)