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))        # 2

4.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))      # 2

5.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. 自测问题

  1. 什么是继承?它有什么好处?
  2. 多态的含义是什么?
  3. 如何实现封装?
  4. 什么是魔术方法?举几个例子。
  5. super() 的作用是什么?

7. 下集预告

下一集我们将学习Python的文件操作!

参考资料

« 上一篇 Python类与对象入门 下一篇 » Python文件操作