目录

第五章 面向对象编程

本章概要

面向对象编程(OOP)是一种程序设计范式,通过“对象”来组织代码。本章将学习Python中类和对象的基本概念,包括封装、继承和多态等核心特性。

5.1 面向对象基础

5.1.1 什么是面向对象

面向对象编程的核心概念:

5.1.2 定义类

使用 ``class`` 关键字定义类。

class Dog:
    """狗的类"""
 
    # 类属性(所有实例共享)
    species = "Canis familiaris"
 
    # 构造方法
    def __init__(self, name, age):
        """初始化方法"""
        self.name = name  # 实例属性
        self.age = age
 
    # 实例方法
    def bark(self):
        """狗叫"""
        return f"{self.name} says: Woof!"
 
    def get_info(self):
        """获取狗的信息"""
        return f"{self.name} is {self.age} years old"
 
# 创建对象
buddy = Dog("Buddy", 3)
miles = Dog("Miles", 5)
 
# 访问属性和方法
print(buddy.name)        # Buddy
print(buddy.bark())      # Buddy says: Woof!
print(buddy.get_info())  # Buddy is 3 years old

5.1.3 __init__ 构造方法

class Person:
    def __init__(self, name, age=0):
        """
        构造方法
        self代表类的实例,必须作为第一个参数
        """
        self.name = name
        self.age = age
        self._id = None  # 内部属性
 
    def introduce(self):
        return f"Hi, I'm {self.name}, {self.age} years old."
 
# 创建对象
person1 = Person("Alice", 25)
person2 = Person("Bob")  # 使用默认年龄

5.1.4 self 详解

class Example:
    def __init__(self, value):
        self.value = value  # 给实例添加属性
 
    def show(self):
        print(f"Value: {self.value}")
 
    def update(self, new_value):
        self.value = new_value
 
# self会自动传递
obj = Example(10)
obj.show()       # 实际是 Example.show(obj)
obj.update(20)   # 实际是 Example.update(obj, 20)

5.2 类的属性

5.2.1 实例属性 vs 类属性

class Car:
    # 类属性 - 所有实例共享
    wheels = 4
 
    def __init__(self, brand, color):
        # 实例属性 - 每个实例独立
        self.brand = brand
        self.color = color
        self.odometer = 0
 
# 使用
car1 = Car("Toyota", "Red")
car2 = Car("Honda", "Blue")
 
print(car1.wheels)  # 4
print(car2.wheels)  # 4
 
# 修改类属性
Car.wheels = 3
print(car1.wheels)  # 3
print(car2.wheels)  # 3
 
# 给实例添加属性(不影响其他实例)
car1.wheels = 5
print(car1.wheels)  # 5(实例属性)
print(car2.wheels)  # 3(类属性)

5.2.2 属性访问控制

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self._balance = balance      # 约定:受保护属性
        self.__transaction_log = []  # 名称修饰:私有属性
 
    def deposit(self, amount):
        if amount > 0:
            self._balance += amount
            self.__transaction_log.append(f"+{amount}")
 
    def get_balance(self):
        return self._balance
 
account = BankAccount("Alice", 100)
 
# 可以访问,但不建议
print(account._balance)  # 100
 
# 名称修饰后的私有属性
# print(account.__transaction_log)  # AttributeError
print(account._BankAccount__transaction_log)  # 可以访问,但不建议

5.2.3 @property 装饰器

class Temperature:
    def __init__(self, celsius=0):
        self._celsius = celsius
 
    @property
    def celsius(self):
        """获取摄氏度"""
        return self._celsius
 
    @celsius.setter
    def celsius(self, value):
        """设置摄氏度"""
        if value < -273.15:
            raise ValueError("温度不能低于绝对零度")
        self._celsius = value
 
    @property
    def fahrenheit(self):
        """获取华氏度(计算属性)"""
        return self._celsius * 9/5 + 32
 
    @fahrenheit.setter
    def fahrenheit(self, value):
        """设置华氏度"""
        self._celsius = (value - 32) * 5/9
 
# 使用
temp = Temperature(25)
print(temp.celsius)      # 25(像属性一样访问)
temp.celsius = 30        # 像属性一样设置
print(temp.fahrenheit)   # 86.0

5.3 类的方法

5.3.1 实例方法

class Circle:
    pi = 3.14159
 
    def __init__(self, radius):
        self.radius = radius
 
    # 实例方法 - 可以访问实例和类
    def area(self):
        return self.pi * self.radius ** 2
 
    def circumference(self):
        return 2 * self.pi * self.radius

5.3.2 类方法

class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
 
    @classmethod
    def from_string(cls, date_string):
        """从字符串创建日期对象"""
        year, month, day = map(int, date_string.split('-'))
        return cls(year, month, day)
 
    @classmethod
    def today(cls):
        """创建今天的日期"""
        import datetime
        now = datetime.datetime.now()
        return cls(now.year, now.month, now.day)
 
    def __str__(self):
        return f"{self.year}-{self.month:02d}-{self.day:02d}"
 
# 使用
birthday = Date.from_string("1990-05-15")
today = Date.today()
print(birthday)  # 1990-05-15
print(today)

5.3.3 静态方法

class MathUtils:
    @staticmethod
    def add(x, y):
        """静态方法,不需要self或cls"""
        return x + y
 
    @staticmethod
    def is_prime(n):
        if n < 2:
            return False
        for i in range(2, int(n**0.5) + 1):
            if n % i == 0:
                return False
        return True
 
# 使用(通过类或实例调用都可以)
print(MathUtils.add(3, 5))      # 8
print(MathUtils.is_prime(17))   # True

5.4 继承

5.4.1 基本继承

# 父类(基类)
class Animal:
    def __init__(self, name):
        self.name = name
 
    def speak(self):
        raise NotImplementedError("子类必须实现此方法")
 
    def introduce(self):
        return f"I am {self.name}"
 
# 子类(派生类)
class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)  # 调用父类构造方法
        self.breed = breed
 
    def speak(self):
        return f"{self.name} says: Woof!"
 
class Cat(Animal):
    def speak(self):
        return f"{self.name} says: Meow!"
 
# 使用
dog = Dog("Buddy", "Golden Retriever")
cat = Cat("Whiskers")
 
print(dog.speak())       # Buddy says: Woof!
print(cat.speak())       # Whiskers says: Meow!
print(dog.introduce())   # I am Buddy(继承的方法)

5.4.2 super() 函数

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
 
    def area(self):
        return self.width * self.height
 
    def perimeter(self):
        return 2 * (self.width + self.height)
 
class Square(Rectangle):
    def __init__(self, side):
        super().__init__(side, side)  # 调用父类构造
        self.side = side
 
    def area(self):
        # 调用父类方法
        return super().area()
 
    def diagonal(self):
        return self.side * (2 ** 0.5)
 
square = Square(5)
print(square.area())       # 25
print(square.perimeter())  # 20

5.4.3 多重继承

class Flyable:
    def fly(self):
        return "I can fly!"
 
class Swimmable:
    def swim(self):
        return "I can swim!"
 
class Duck(Animal, Flyable, Swimmable):
    def speak(self):
        return f"{self.name} says: Quack!"
 
duck = Duck("Donald")
print(duck.speak())  # Donald says: Quack!
print(duck.fly())    # I can fly!
print(duck.swim())   # I can swim!

5.4.4 方法解析顺序(MRO)

class A:
    def method(self):
        return "A"
 
class B(A):
    def method(self):
        return "B"
 
class C(A):
    def method(self):
        return "C"
 
class D(B, C):
    pass
 
print(D.method())  # B(按MRO顺序)
print(D.__mro__)   # (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)

5.5 多态

5.5.1 鸭子类型

Python是动态类型语言,通过“鸭子类型”实现多态。

class Dog:
    def speak(self):
        return "Woof!"
 
    def move(self):
        return "Running on 4 legs"
 
class Bird:
    def speak(self):
        return "Tweet!"
 
    def move(self):
        return "Flying in the sky"
 
class Fish:
    def speak(self):
        return "Blub!"
 
    def move(self):
        return "Swimming in water"
 
# 多态函数
def animal_concert(animals):
    for animal in animals:
        print(f"{type(animal).__name__}: {animal.speak()}")
 
# 不同的对象,相同的方法调用
animals = [Dog(), Bird(), Fish()]
animal_concert(animals)

5.5.2 抽象基类

from abc import ABC, abstractmethod
 
class Shape(ABC):
    @abstractmethod
    def area(self):
        """计算面积"""
        pass
 
    @abstractmethod
    def perimeter(self):
        """计算周长"""
        pass
 
class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height
 
    def area(self):
        return self.width * self.height
 
    def perimeter(self):
        return 2 * (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
 
    def perimeter(self):
        import math
        return 2 * math.pi * self.radius
 
# shape = Shape()  # 错误!不能实例化抽象类
rect = Rectangle(3, 4)
circle = Circle(5)
 
shapes = [rect, circle]
for shape in shapes:
    print(f"{type(shape).__name__}: Area={shape.area():.2f}")

5.6 封装

5.6.1 数据封装

class BankAccount:
    def __init__(self, owner, balance=0):
        self._owner = owner
        self._balance = balance
 
    @property
    def balance(self):
        """只读属性:余额"""
        return self._balance
 
    @property
    def owner(self):
        """只读属性:所有者"""
        return self._owner
 
    def deposit(self, amount):
        """存款"""
        if amount <= 0:
            raise ValueError("存款金额必须大于0")
        self._balance += amount
        return self._balance
 
    def withdraw(self, amount):
        """取款"""
        if amount <= 0:
            raise ValueError("取款金额必须大于0")
        if amount > self._balance:
            raise ValueError("余额不足")
        self._balance -= amount
        return self._balance
 
# 使用
account = BankAccount("Alice", 1000)
account.deposit(500)
account.withdraw(200)
print(account.balance)  # 1300
# account.balance = 5000  # AttributeError: can't set attribute

5.7 代码示例

示例1:图书管理系统

from datetime import datetime, timedelta
 
class Book:
    def __init__(self, isbn, title, author):
        self.isbn = isbn
        self.title = title
        self.author = author
        self.is_borrowed = False
        self.due_date = None
 
    def borrow(self, days=14):
        if self.is_borrowed:
            return False
        self.is_borrowed = True
        self.due_date = datetime.now() + timedelta(days=days)
        return True
 
    def return_book(self):
        if not self.is_borrowed:
            return False
        self.is_borrowed = False
        self.due_date = None
        return True
 
    def __str__(self):
        status = "已借出" if self.is_borrowed else "可借阅"
        return f"《{self.title}》- {self.author} [{status}]"
 
class Member:
    def __init__(self, member_id, name):
        self.member_id = member_id
        self.name = name
        self.borrowed_books = []
        self.max_books = 5
 
    def borrow_book(self, book):
        if len(self.borrowed_books) >= self.max_books:
            return False, "已达最大借书数量"
        if book.borrow():
            self.borrowed_books.append(book)
            return True, f"成功借阅《{book.title}》"
        return False, "该书已被借出"
 
    def return_book(self, book):
        if book in self.borrowed_books:
            book.return_book()
            self.borrowed_books.remove(book)
            return True, f"成功归还《{book.title}》"
        return False, "未借阅该书"
 
# 使用示例
library = []
member = Member("M001", "张三")
 
book1 = Book("978-7-111", "Python编程", "张三")
book2 = Book("978-7-112", "数据结构与算法", "李四")
library.extend([book1, book2])
 
success, msg = member.borrow_book(book1)
print(msg)
print(book1)

示例2:几何图形类

import math
from abc import ABC, abstractmethod
 
class Shape(ABC):
    @abstractmethod
    def area(self):
        pass
 
    @abstractmethod
    def perimeter(self):
        pass
 
    def __str__(self):
        return f"{self.__class__.__name__}: area={self.area():.2f}"
 
class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height
 
    def area(self):
        return self.width * self.height
 
    def perimeter(self):
        return 2 * (self.width + self.height)
 
class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
 
    def area(self):
        return math.pi * self.radius ** 2
 
    def perimeter(self):
        return 2 * math.pi * self.radius
 
class Triangle(Shape):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c
 
    def area(self):
        s = self.perimeter() / 2
        return math.sqrt(s * (s - self.a) * (s - self.b) * (s - self.c))
 
    def perimeter(self):
        return self.a + self.b + self.c
 
def calculate_total_area(shapes):
    return sum(shape.area() for shape in shapes)
 
# 使用
shapes = [Rectangle(3, 4), Circle(5), Triangle(3, 4, 5)]
for shape in shapes:
    print(shape)
print(f"总面积: {calculate_total_area(shapes):.2f}")

5.8 练习题

练习1:实现一个Vector类

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"Vector({self.x}, {self.y})"
 
    def magnitude(self):
        return (self.x ** 2 + self.y ** 2) ** 0.5
 
# 测试
v1 = Vector(3, 4)
v2 = Vector(1, 2)
print(v1 + v2)        # Vector(4, 6)
print(v1 * 2)         # Vector(6, 8)
print(v1.magnitude()) # 5.0

练习2:实现栈(Stack)类

class Stack:
    def __init__(self):
        self._items = []
 
    def push(self, item):
        self._items.append(item)
 
    def pop(self):
        if self.is_empty():
            raise IndexError("栈为空")
        return self._items.pop()
 
    def peek(self):
        if self.is_empty():
            raise IndexError("栈为空")
        return self._items[-1]
 
    def is_empty(self):
        return len(self._items) == 0
 
    def size(self):
        return len(self._items)
 
    def __str__(self):
        return f"Stack({self._items})"
 
# 测试
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
print(stack.pop())  # 3
print(stack)        # Stack([1, 2])

本章小结

本章学习了面向对象编程的核心概念:

面向对象编程让代码更加模块化、可维护和可扩展。下一章将学习更多高级OOP特性。

进一步阅读