Python 百战 入门 8 面向对象编程进阶 封装,继承,多态
继承
子类扩展父类
Python支持多重继承,一个子类可以继承多个父类
如果在类定义中没有指定父类,则默认父类是 object类
class 子类类名(父类1[,父类2,...]):
类体
构造函数
子类不重写 init ,实例化子类时,会自动调用父类定义的 init 。
子类重写了 init 时,实例化子类,就不会调用父类已经定义的 init
如果重写了 init 时,要使用父类的构造方法,可以使用 super 关键字,也可以使用如下格式调用:
父类名.init(self, 参数列表)
class Person:
def init(self, name, age): # Person类的构造方法,接收name和age
print("Person的构造方法") # 提示构造方法执行
self.name = name # 初始化实例变量name
self.age = age # 初始化实例变量age
def say_age(self): # 定义方法say_age
print(self.name, "的年龄是:", self.age) # 打印姓名和年龄
class Student(Person): # Student继承Person
def init(self, name, age, score): # 子类构造方法,新增score
super(Student, self).init(name, age) # 调用父类构造方法
print("Student的构造方法") # 提示子类构造方法执行
self.score = score # 初始化子类特有变量score
s1 = Student("张三", 15, 85) # 创建Student实例
print(dir(s1)) # 打印对象属性和方法列表
类成员的继承和重写
1 成员继承:子类继承了父类除构造方法之外的所有成员。(私有属性、私有方法也被继承)
2 方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为“重写”
class Person:
def init(self, name, age): # 人类基类构造方法
self.name = name # 初始化姓名属性
self.age = age # 初始化年龄属性
def say_age(self): # 年龄汇报方法
print(self.name, "的年龄是:", self.age) # 打印姓名和年龄信息
def say_name(self): # 姓名汇报方法(父类版本)
print("我是", self.name) # 标准自我介绍格式
class Student(Person):
def init(self, name, age, score): # 学生类构造方法
Person.init(self, name, age) # 调用父类构造方法初始化基础属性
self.score = score # 新增分数属性初始化
def say_score(self): # 分数汇报方法(子类特有)
print(self.name, "的分数是:", self.score) # 打印姓名和分数信息
def say_name(self): # 重写父类的say_name方法
print("报告老师,我是", self.name) # 学生特有自我介绍格式
实例化测试
s1 = Student("张三", 15, 85) # 创建学生对象
s1.say_score() # 调用子类特有方法
s1.say_name() # 调用重写后的方法
s1.say_age() # 调用继承自父类的方法
object根类
object 类是所有类的父类,因此所有的类都有 object 类的属性和方法。
dir() 查看对象属性
重写 str() 方法
多重继承
class A:
def init(self):
print("A 初始化")
class B(A):
def init(self):
super().init()
print("B 初始化")
class C(A):
def init(self):
super().init()
print("C 初始化")
class D(B, C):
def init(self):
super().init()
print("D 初始化")
d = D() # 输出顺序:ACBD
MRO方法解析顺序
先左后右
super()获得父类定义
在子类中,如果想要获得父类的方法时,我们可以通过 super() 来做。super() 代表父类的定义,不是父类对象。
❤想调用父类的构造方法:
super(子类名称,self).init(参数列表)
多态
多态(polymorphism)是指同一个方法调用由于对象不同可能会产生不同的行为。
多态是方法的多态,属性没有多态。
多态的存在有2个必要条件:继承、方法重写
class Animal: # 定义动物基类
def shout(self): # 动物叫声方法
print("动物叫了一声") # 默认实现
class Dog(Animal): # 定义狗子类
def shout(self): # 重写叫声方法
print("小狗,汪汪汪") # 狗的特有叫声
class Cat(Animal): # 定义猫子类
def shout(self): # 重写叫声方法
print("小猫,喵喵喵") # 猫的特有叫声
def animal_shout(a: Animal): # 类型提示:接收Animal类型
"""多态演示函数:根据传入对象调用不同shout方法"""
a.shout() # 动态调用实际对象的shout方法
多态调用演示
animal_shout(Dog()) # 传入Dog对象,输出:小狗,汪汪汪
animal_shout(Cat()) # 传入Cat对象,输出:小猫,喵喵喵
animal_shout(Animal()) # 传入基类对象,输出:动物叫了一声
特殊方法和运算符重载
常见特殊方法


运算符重载
运算符重载示例
class Person:
def init(self, name):
self.name = name
def __add__(self, other):
if isinstance(other, Person):
return f"{self.name}--{other.name}"
return "操作数类型错误"
def __mul__(self, other):
if isinstance(other, int):
return self.name * other
return "操作数类型错误"
使用示例
运算符重载示例
class Person:
def init(self, name): # 初始化方法
self.name = name # 设置姓名属性
def __add__(self, other): # 重载加法运算符
if isinstance(other, Person): # 检查是否为Person对象
return f"{self.name}--{other.name}" # 返回连接后的姓名
return "操作数类型错误" # 类型不匹配时返回错误
def __mul__(self, other): # 重载乘法运算符
if isinstance(other, int): # 检查是否为整数
return self.name * other # 返回重复的姓名
return "操作数类型错误" # 类型不匹配时返回错误
使用示例
p1 = Person("高淇") # 创建第一个Person对象
p2 = Person("高希希") # 创建第二个Person对象
print(p1 + p2) # 调用加法运算符重载
print(p1 * 3) # 调用乘法运算符重载
特殊属性
ython对象中包含了很多双下划线开始和结束的属性,这些是特殊属性,有特殊用法。这里我们列出常见的特殊属性:

对象的浅拷贝和深拷贝
import copy # 导入拷贝模块
class CPU: #CPU类,作为MobilePhone的组件
pass
class MobilePhone: #手机类,包含一个CPU组件
def init(self, cpu):
self.cpu = cpu # 初始化时传入CPU对象
创建原始对象
c = CPU() # 实例化一个CPU对象
m = MobilePhone(c) # 创建包含该CPU的手机对象
print("---- 引用赋值 ----")
m_ref = m # 简单的引用赋值
print("m的内存地址:", id(m)) # 输出原始对象地址
print("m_ref的内存地址:", id(m_ref)) # 与m相同
print("m.cpu的内存地址:", id(m.cpu)) # 组件地址
print("m_ref.cpu的内存地址:", id(m_ref.cpu)) # 组件地址相同
print("\n---- 浅拷贝 ----")
m2 = copy.copy(m) # 执行浅拷贝
print("m的内存地址:", id(m)) # 原始对象不变
print("m2的内存地址:", id(m2)) # 新对象地址不同
print("m.cpu的内存地址:", id(m.cpu)) # 原始组件
print("m2.cpu的内存地址:", id(m2.cpu)) # 组件地址相同(浅拷贝特性)
print("\n---- 深拷贝 ----")
m3 = copy.deepcopy(m) # 执行深拷贝
print("m的内存地址:", id(m)) # 原始对象不变
print("m3的内存地址:", id(m3)) # 新对象地址不同
print("m.cpu的内存地址:", id(m.cpu)) # 原始组件
print("m3.cpu的内存地址:", id(m3.cpu)) # 新组件地址不同(深拷贝特性)
组合
除了继承,“组合”也能实现代码的复用!“组合”核心是“将父类对象作为子类的属性”。
is-a 关系,我们可以使用“继承”。从而实现子类拥有的父类的方法和属性。 is-a 关系指的是类似这样的关系:狗是动物,dog is animal。狗类就应该继承动物类。
has-a 关系,我们可以使用“组合”,也能实现一个类拥有另一个类的方法和属性。 has-a 关系指的是这样的关系:手机拥有CPU。 MobilePhone has a CPU
class CPU:
def calculate(self):
print("CPU计算中...")
class Screen:
def show(self):
print("屏幕显示中...")
class MobilePhone:
def init(self, cpu, screen):
self.cpu = cpu
self.screen = screen
创建组件
c = CPU()
s = Screen()
组装手机
m = MobilePhone(c, s)
使用功能
m.cpu.calculate()
m.screen.show()
设计模式_工厂模式实现
工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进行统一的管理和控制。
想象这是一家汽车4S店(工厂)
class CarShop:
@staticmethod
def get_car(money):
if money > 1000000: # 预算充足
return LuxuryCar() # 给你豪车
elif money > 200000: # 预算中等
return FamilyCar() # 给你家用车
else: # 预算有限
return MiniCar() # 给你小车
各种车型(不需要知道具体怎么造车)
class LuxuryCar:
def drive(self):
print("开豪车:轰隆隆~")
class FamilyCar:
def drive(self):
print("开家用车:滴滴~")
class MiniCar:
def drive(self):
print("开小车:突突突...")
你作为顾客(调用者)
if name == "main":
告诉4S店你的预算,直接提车
my_car = CarShop.get_car(300000) # 30万预算
my_car.drive() # 输出:开家用车:滴滴~
设计模式_单例模式实现
单例模式(Singleton Pattern)的核心作用是确保一个类只有一个实例,并且提供一个访问该实例的全局访问点。
单例模式只生成一个实例对象,减少了对系统资源的开销。当一个对象的产生需要比较多的资源,如读取配置文件、产生其他依赖对象时,可以产生一个“单例对象”,然后永久驻留内存中,从而极大的降低开销。
⚠单例模式有多种实现的方式,我们这里推荐重写 new() 的方法。
class 饮水机: # 定义一个饮水机类
_唯一饮水机 = None # 类变量,用来保存唯一的饮水机实例
def __new__(cls): # 重写__new__方法控制实例创建
if not cls._唯一饮水机: # 如果还没有饮水机
cls._唯一饮水机 = super().__new__(cls) # 创建一个新的饮水机
cls._唯一饮水机.水量 = 1000 # 初始化水量为1000ml
return cls._唯一饮水机 # 返回这个唯一的饮水机
def 接水(self, 毫升): # 定义接水方法
if self.水量 >= 毫升: # 如果水量足够
self.水量 -= 毫升 # 减少水量
print(f"接出{毫升}ml水,剩余{self.水量}ml") # 打印接水信息
else: # 如果水量不足
print("水量不足!") # 提示水量不足
使用示例
饮水机1 = 饮水机() # 第一次获取饮水机
饮水机1.接水(200) # 从饮水机接200ml水
饮水机2 = 饮水机() # 第二次获取饮水机
饮水机2.接水(300) # 从饮水机接300ml水
print(饮水机1 is 饮水机2) # 检查是否是同一个饮水机,输出True
工厂和单例模式结合
只需要简单的套用即可实现:
饮料工厂(单例模式确保只有一个工厂)
class 饮料工厂:
_唯一工厂 = None # 保存唯一工厂实例
def __new__(cls): # 控制实例创建
if not cls._唯一工厂: # 如果还没有工厂
cls._唯一工厂 = super().__new__(cls) # 创建唯一工厂
cls._唯一工厂.饮料配方 = { # 初始化配方库
"可乐": ["糖浆", "碳酸水"],
"雪碧": ["柠檬酸", "碳酸水"],
"芬达": ["橙汁", "碳酸水"]
}
return cls._唯一工厂 # 返回唯一工厂
def 生产饮料(self, 类型): # 生产饮料方法
if 类型 in self.饮料配方: # 检查是否能生产
原料 = self.饮料配方[类型] # 获取原料
print(f"正在用{原料}生产{类型}") # 打印生产信息
return 饮料(类型, 原料) # 返回饮料对象
else: # 没有配方的情况
print(f"无法生产{类型}") # 提示无法生产
return None # 返回空值
饮料产品类
class 饮料:
def init(self, 名称, 原料): # 初始化方法
self.名称 = 名称 # 设置饮料名称
self.原料 = 原料 # 设置原料列表
def 描述(self): # 描述方法
print(f"这是一瓶{self.名称},由{self.原料}制成") # 打印描述
使用示例
if name == "main":
工厂1 = 饮料工厂() # 第一次获取工厂
工厂2 = 饮料工厂() # 第二次获取工厂
print(工厂1 is 工厂2) # 检查是否是同一工厂,输出True
可乐 = 工厂1.生产饮料("可乐") # 生产可乐
雪碧 = 工厂2.生产饮料("雪碧") # 生产雪碧
未知饮料 = 工厂1.生产饮料("奶茶") # 尝试生产不存在的饮料
if 可乐: # 如果生产成功
可乐.描述() # 描述可乐
if 雪碧: # 如果生产成功
雪碧.描述() # 描述雪碧
浙公网安备 33010602011771号