Python面向对象进阶
学习目标
通过本集的学习,你将能够:
- 理解和使用继承
- 掌握多态的概念和应用
- 理解封装的实现方式
- 使用常用的魔术方法
- 设计合理的类层次结构
1. 继承
继承允许一个类继承另一个类的属性和方法,实现代码复用和层次化设计。
1.1 基本继承
# 父类(基类)
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print(f"{self.name} 在吃东西")
def sleep(self):
print(f"{self.name} 在睡觉")
# 子类(派生类)继承自 Animal
class Dog(Animal):
def bark(self):
print(f"{self.name} 在汪汪叫")
class Cat(Animal):
def meow(self):
print(f"{self.name} 在喵喵叫")
# 使用
dog = Dog("旺财", 3)
dog.eat() # 继承自 Animal
dog.sleep() # 继承自 Animal
dog.bark() # Dog 特有的方法
cat = Cat("咪咪", 2)
cat.eat()
cat.meow()继承的ASCII图:
┌─────────────────┐
│ Animal │ 父类
├─────────────────┤
│ name │
│ age │
├─────────────────┤
│ eat() │
│ sleep() │
└─────────────────┘
▲
│ 继承
┌─────┴─────┐
│ │
┌───────────┐ ┌───────────┐
│ Dog │ │ Cat │ 子类
├───────────┤ ├───────────┤
│ bark() │ │ meow() │
└───────────┘ └───────────┘1.2 方法重写
class Animal:
def __init__(self, name):
self.name = name
def make_sound(self):
print("动物发出声音")
class Dog(Animal):
def make_sound(self):
# 重写父类方法
print(f"{self.name} 汪汪叫")
class Cat(Animal):
def make_sound(self):
# 重写父类方法
print(f"{self.name} 喵喵叫")
# 使用
animals = [Dog("旺财"), Cat("咪咪"), Animal("未知动物")]
for animal in animals:
animal.make_sound()1.3 super() 调用父类方法
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
print(f"Animal 初始化: {name}")
def eat(self):
print(f"{self.name} 在吃东西")
class Dog(Animal):
def __init__(self, name, age, breed):
# 调用父类的 __init__
super().__init__(name, age)
self.breed = breed
print(f"Dog 初始化: {breed}")
def eat(self):
# 先调用父类的 eat
super().eat()
print(f"{self.name} 在吃狗粮")
dog = Dog("旺财", 3, "金毛")
dog.eat()1.4 多层继承
class Animal:
def __init__(self, name):
self.name = name
class Mammal(Animal):
def feed_milk(self):
print(f"{self.name} 哺乳")
class Dog(Mammal):
def bark(self):
print(f"{self.name} 汪汪叫")
dog = Dog("旺财")
dog.feed_milk() # 继承自 Mammal
dog.bark() # Dog 特有1.5 多重继承
class Flyable:
def fly(self):
print("我会飞")
class Swimmable:
def swim(self):
print("我会游泳")
class Duck(Flyable, Swimmable):
def quack(self):
print("嘎嘎嘎")
duck = Duck()
duck.fly()
duck.swim()
duck.quack()
# 方法解析顺序(MRO)
print(Duck.__mro__)2. 多态
多态允许不同类的对象对同一方法做出不同的响应。
2.1 多态示例
class Shape:
def area(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
import math
return math.pi * self.radius ** 2
class Triangle(Shape):
def __init__(self, base, height):
self.base = base
self.height = height
def area(self):
return 0.5 * self.base * self.height
# 多态:不同对象调用同一个方法名,表现不同
shapes = [
Rectangle(5, 3),
Circle(4),
Triangle(6, 4)
]
for shape in shapes:
print(f"面积: {shape.area():.2f}")2.2 鸭子类型
# Python 的鸭子类型:"如果它走起路来像鸭子,叫起来也是鸭子,那它就是鸭子"
class Duck:
def quack(self):
print("嘎嘎嘎")
def swim(self):
print("鸭子在游泳")
class Person:
def quack(self):
print("人在模仿鸭子叫")
def swim(self):
print("人在游泳")
def make_it_quack_and_swim(obj):
obj.quack()
obj.swim()
duck = Duck()
person = Person()
make_it_quack_and_swim(duck)
make_it_quack_and_swim(person)3. 封装
封装将数据和操作数据的方法绑定在一起,并隐藏内部实现细节。
3.1 私有属性和方法
class BankAccount:
def __init__(self, initial_balance):
self._balance = initial_balance # 单下划线:受保护
self.__secret = "这是秘密" # 双下划线:私有(名称修饰)
def deposit(self, amount):
if amount > 0:
self._balance += amount
def get_balance(self):
return self._balance
def __private_method(self):
print("这是私有方法")
def public_method(self):
print("这是公共方法")
self.__private_method() # 内部可以调用
account = BankAccount(1000)
print(account.get_balance())
# 可以访问 _balance,但不建议
print(account._balance)
# 不能直接访问 __secret
# print(account.__secret) # 错误
# 但可以通过名称修饰访问(不推荐)
print(account._BankAccount__secret)
account.public_method()3.2 @property 装饰器
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""获取半径"""
return self._radius
@radius.setter
def radius(self, value):
"""设置半径"""
if value > 0:
self._radius = value
else:
raise ValueError("半径必须大于0")
@property
def area(self):
"""计算面积(只读属性)"""
import math
return math.pi * self._radius ** 2
circle = Circle(5)
print(circle.radius) # 使用 getter
print(circle.area) # 只读属性
circle.radius = 10 # 使用 setter
print(circle.radius)
print(circle.area)
# circle.area = 100 # 错误:只读属性4. 魔术方法
魔术方法(特殊方法)以双下划线开头和结尾,用于实现对象的特殊行为。
4.1 字符串表示
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
"""用户友好的字符串表示"""
return f"{self.name}, {self.age}岁"
def __repr__(self):
"""开发者友好的字符串表示"""
return f"Person('{self.name}', {self.age})"
person = Person("张三", 25)
print(str(person)) # 输出: 张三, 25岁
print(repr(person)) # 输出: Person('张三', 25)4.2 比较运算符
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def __eq__(self, other):
"""等于 == """
return self.score == other.score
def __lt__(self, other):
"""小于 < """
return self.score < other.score
def __le__(self, other):
"""小于等于 <= """
return self.score <= other.score
def __gt__(self, other):
"""大于 > """
return self.score > other.score
def __ge__(self, other):
"""大于等于 >= """
return self.score >= other.score
def __str__(self):
return f"{self.name}: {self.score}"
s1 = Student("张三", 85)
s2 = Student("李四", 90)
s3 = Student("王五", 85)
print(s1 < s2) # True
print(s1 == s3) # True
print(s2 > s1) # True
students = [s1, s2, s3]
students.sort()
for s in students:
print(s)4.3 容器操作
class MyList:
def __init__(self):
self._items = []
def __len__(self):
"""len()"""
return len(self._items)
def __getitem__(self, index):
"""[] 访问"""
return self._items[index]
def __setitem__(self, index, value):
"""[] 设置"""
self._items[index] = value
def __delitem__(self, index):
"""del []"""
del self._items[index]
def __contains__(self, item):
"""in"""
return item in self._items
def append(self, item):
self._items.append(item)
my_list = MyList()
my_list.append(1)
my_list.append(2)
my_list.append(3)
print(len(my_list)) # 3
print(my_list[0]) # 1
my_list[0] = 10
print(my_list[0]) # 10
print(2 in my_list) # True
del my_list[1]
print(len(my_list)) # 24.4 算术运算符
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
"""加法 +"""
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other):
"""减法 -"""
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar):
"""乘法 *"""
return Vector(self.x * scalar, self.y * scalar)
def __str__(self):
return f"({self.x}, {self.y})"
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # (4, 6)
print(v1 - v2) # (-2, -2)
print(v1 * 2) # (2, 4)4.5 上下文管理器
class FileManager:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
"""进入 with 块时调用"""
self.file = open(self.filename, self.mode)
print("文件已打开")
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
"""退出 with 块时调用"""
if self.file:
self.file.close()
print("文件已关闭")
# 返回 True 表示已处理异常
return False
# 使用
with FileManager("test.txt", "w") as f:
f.write("Hello, World!")
print("写入完成")5. 实用案例
5.1 案例1:员工管理系统
# employee_system.py
class Employee:
"""员工基类"""
def __init__(self, name, employee_id, base_salary):
self.name = name
self.employee_id = employee_id
self.base_salary = base_salary
def calculate_salary(self):
"""计算工资(子类重写)"""
return self.base_salary
def __str__(self):
return f"员工: {self.name} (ID: {self.employee_id})"
class FullTimeEmployee(Employee):
"""全职员工"""
def __init__(self, name, employee_id, base_salary, bonus):
super().__init__(name, employee_id, base_salary)
self.bonus = bonus
def calculate_salary(self):
return self.base_salary + self.bonus
def __str__(self):
return f"全职员工: {self.name} - 工资: {self.calculate_salary()}"
class PartTimeEmployee(Employee):
"""兼职员工"""
def __init__(self, name, employee_id, hourly_rate, hours_worked):
super().__init__(name, employee_id, 0)
self.hourly_rate = hourly_rate
self.hours_worked = hours_worked
def calculate_salary(self):
return self.hourly_rate * self.hours_worked
def __str__(self):
return f"兼职员工: {self.name} - 工资: {self.calculate_salary()}"
class EmployeeManager:
"""员工管理器"""
def __init__(self):
self.employees = {}
def add_employee(self, employee):
self.employees[employee.employee_id] = employee
print(f"已添加: {employee}")
def remove_employee(self, employee_id):
if employee_id in self.employees:
del self.employees[employee_id]
print(f"已删除员工 ID: {employee_id}")
def calculate_total_salary(self):
total = 0
for employee in self.employees.values():
total += employee.calculate_salary()
return total
def show_all(self):
if not self.employees:
print("没有员工")
return
print("\n所有员工:")
for employee in self.employees.values():
print(f" {employee}")
print(f"\n总工资支出: ¥{self.calculate_total_salary()}")
# 使用
manager = EmployeeManager()
manager.add_employee(FullTimeEmployee("张三", "E001", 8000, 2000))
manager.add_employee(FullTimeEmployee("李四", "E002", 10000, 3000))
manager.add_employee(PartTimeEmployee("王五", "E003", 50, 80))
manager.show_all()5.2 案例2:自定义数据结构
# custom_stack.py
class Stack:
"""栈(LIFO)"""
def __init__(self):
self._items = []
def push(self, item):
self._items.append(item)
def pop(self):
if not self.is_empty():
return self._items.pop()
raise IndexError("栈为空")
def peek(self):
if not self.is_empty():
return self._items[-1]
raise IndexError("栈为空")
def is_empty(self):
return len(self._items) == 0
def __len__(self):
return len(self._items)
def __str__(self):
return f"Stack: {self._items}"
def __repr__(self):
return f"Stack({self._items})"
class Queue:
"""队列(FIFO)"""
def __init__(self):
self._items = []
def enqueue(self, item):
self._items.append(item)
def dequeue(self):
if not self.is_empty():
return self._items.pop(0)
raise IndexError("队列为空")
def is_empty(self):
return len(self._items) == 0
def __len__(self):
return len(self._items)
def __str__(self):
return f"Queue: {self._items}"
# 使用栈
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
print(stack) # Stack: [1, 2, 3]
print(stack.pop()) # 3
print(stack.peek()) # 2
print(len(stack)) # 2
# 使用队列
queue = Queue()
queue.enqueue("A")
queue.enqueue("B")
queue.enqueue("C")
print(queue) # Queue: ['A', 'B', 'C']
print(queue.dequeue()) # A
print(len(queue)) # 25.3 案例3:游戏角色系统
# game_characters.py
import random
class Character:
"""游戏角色基类"""
def __init__(self, name, hp, attack, defense):
self.name = name
self.max_hp = hp
self.hp = hp
self.attack = attack
self.defense = defense
self.is_alive = True
def take_damage(self, damage):
"""受到伤害"""
actual_damage = max(1, damage - self.defense)
self.hp -= actual_damage
if self.hp <= 0:
self.hp = 0
self.is_alive = False
return actual_damage
def attack_target(self, target):
"""攻击目标"""
if not self.is_alive:
print(f"{self.name} 已经死亡,无法攻击")
return
damage = random.randint(self.attack - 5, self.attack + 5)
actual_damage = target.take_damage(damage)
print(f"{self.name} 攻击 {target.name},造成 {actual_damage} 点伤害")
if not target.is_alive:
print(f"{target.name} 被击败了!")
def heal(self, amount):
"""治疗"""
if not self.is_alive:
print(f"{self.name} 已经死亡,无法治疗")
return
self.hp = min(self.max_hp, self.hp + amount)
print(f"{self.name} 恢复了 {amount} 点生命值")
def __str__(self):
return f"{self.name} - HP: {self.hp}/{self.max_hp}, ATK: {self.attack}, DEF: {self.defense}"
class Warrior(Character):
"""战士"""
def __init__(self, name):
super().__init__(name, hp=150, attack=25, defense=15)
def shield_bash(self, target):
"""盾牌猛击"""
damage = self.attack * 2
actual_damage = target.take_damage(damage)
print(f"{self.name} 使用盾牌猛击攻击 {target.name},造成 {actual_damage} 点伤害!")
class Mage(Character):
"""法师"""
def __init__(self, name):
super().__init__(name, hp=100, attack=35, defense=5)
self.mp = 50
self.max_mp = 50
def fireball(self, target):
"""火球术"""
if self.mp < 20:
print(f"{self.name} 魔法不足!")
return
self.mp -= 20
damage = self.attack * 3
actual_damage = target.take_damage(damage)
print(f"{self.name} 释放火球术攻击 {target.name},造成 {actual_damage} 点伤害!")
def __str__(self):
return f"{super().__str__()}, MP: {self.mp}/{self.max_mp}"
# 游戏演示
print("=== 游戏开始 ===")
warrior = Warrior("亚瑟")
mage = Mage("梅林")
print("\n初始状态:")
print(warrior)
print(mage)
print("\n=== 战斗开始 ===")
warrior.attack_target(mage)
mage.fireball(warrior)
warrior.shield_bash(mage)
mage.attack_target(warrior)
print("\n战斗后状态:")
print(warrior)
print(mage)6. 自测问题
- 什么是继承?它有什么好处?
- 多态的含义是什么?
- 如何实现封装?
- 什么是魔术方法?举几个例子。
- super() 的作用是什么?
7. 下集预告
下一集我们将学习Python的文件操作!
参考资料
- Python官方文档: https://docs.python.org/3/tutorial/classes.html
- 《Python编程:从入门到实践》