Administrator
发布于 2026-03-19 / 1 阅读
0
0

第十章:面向对象基础

第十章:面向对象基础

本章学习目标

  • 理解类和对象的基本概念
  • 掌握类的定义和实例化
  • 理解封装、继承、多态三大特性
  • 学会使用 dataclasses 简化类定义
  • 理解特殊方法和运算符重载

10.1 类与对象基础

10.1.1 什么是面向对象

面向对象编程(OOP)是一种将数据和操作数据的方法封装在一起的编程范式。Python 是一门多范式语言,完全支持面向对象编程。

面向对象的核心概念:

  • 类 (Class):对象的蓝图,定义对象的属性和方法
  • 对象 (Object):类的实例,具有具体的数据
  • 属性 (Attribute):类或对象的特征
  • 方法 (Method):类中定义的函数

10.1.2 定义类

class Person:
    """人类"""

    # 类属性(在所有实例间共享)
    species: str = "Human"

    # 实例属性(在 __init__ 中定义)
    def __init__(self, name: str, age: int) -> None:
        self.name: str = name
        self.age: int = age

    # 实例方法
    def greet(self) -> str:
        return f"Hello, I'm {self.name}"

    def birthday(self) -> None:
        self.age += 1

    # 特殊方法
    def __str__(self) -> str:
        return f"Person(name={self.name}, age={self.age})"

    def __repr__(self) -> str:
        return f"Person(name={self.name!r}, age={self.age!r})"

10.1.3 创建和使用对象

# 创建实例
person = Person("Alice", 25)

# 访问实例属性
print(person.name)  # Alice
print(person.age)   # 25

# 访问类属性
print(person.species)        # Human
print(Person.species)        # Human

# 调用方法
print(person.greet())  # Hello, I'm Alice

# 修改实例属性
person.age = 26
person.birthday()
print(person.age)  # 27

# 打印对象
print(person)  # Person(name=Alice, age=27)

10.1.4 self 参数

self 指向对象实例本身,类似于其他语言中的 this

class Calculator:
    def add(self, a: float, b: float) -> float:
        # self 指向调用此方法的对象
        return a + b

# 调用时,Python 自动传递实例
calc = Calculator()
result = calc.add(3, 5)  # 等价于 Calculator.add(calc, 3, 5)

10.2 封装

10.2.1 什么是封装

封装是将数据和操作封装在一起,并对外部隐藏内部实现细节。封装可以:

  • 保护数据不被意外修改
  • 提供清晰的接口
  • 便于代码维护

10.2.2 访问控制

Python 使用命名约定来实现访问控制:

class BankAccount:
    def __init__(self, account_id: str, balance: float = 0.0) -> None:
        self.account_id: str = account_id      # 公开
        self._balance: float = balance          # 受保护(约定)
        self.__balance: float = balance         # 私有(名称重整)

    def get_balance(self) -> float:
        """公开的获取余额方法"""
        return self._balance

    def deposit(self, amount: float) -> None:
        """存款"""
        if amount <= 0:
            raise ValueError("存款金额必须为正数")
        self._balance += amount

    def withdraw(self, amount: float) -> None:
        """取款"""
        if amount <= 0:
            raise ValueError("取款金额必须为正数")
        if amount > self._balance:
            raise ValueError("余额不足")
        self._balance -= amount


# 使用
account = BankAccount("001", 1000)
print(account.get_balance())  # 1000
account.deposit(500)
print(account.get_balance())  # 1500
account.withdraw(200)
print(account.get_balance())  # 1300

# 访问受保护属性(不推荐)
print(account._balance)

# 访问私有属性(会失败,Python 会重命名)
# print(account.__balance)  # AttributeError
# 可以通过 _ClassName__attribute 访问
print(account._BankAccount__balance)

10.2.3 property 装饰器

使用 @property 实现属性的 getter、setter 和 deleter:

class Temperature:
    """温度类"""

    def __init__(self, celsius: float = 0.0) -> None:
        self._celsius: float = celsius

    # getter
    @property
    def celsius(self) -> float:
        return self._celsius

    # setter
    @celsius.setter
    def celsius(self, value: float) -> None:
        if value < -273.15:
            raise ValueError("温度不能低于绝对零度")
        self._celsius = value

    # 计算属性(华氏温度)
    @property
    def fahrenheit(self) -> float:
        return self._celsius * 9 / 5 + 32

    @fahrenheit.setter
    def fahrenheit(self, value: float) -> None:
        self._celsius = (value - 32) * 5 / 9

    # deleter
    @celsius.deleter
    def celsius(self) -> None:
        print("删除温度值")
        self._celsius = 0


# 使用
temp = Temperature(25)
print(temp.celsius)      # 25
print(temp.fahrenheit)   # 77.0

temp.fahrenheit = 100
print(temp.celsius)     # 37.777...

del temp.celsius        # 删除温度值
print(temp.celsius)     # 0

10.3 继承

10.3.1 什么是继承

继承允许创建基于现有类的新类,新类(子类)继承父类的属性和方法。

class Animal:
    """动物基类"""

    def __init__(self, name: str) -> None:
        self.name = name

    def speak(self) -> str:
        return "..."

    def __str__(self) -> str:
        return f"Animal: {self.name}"


class Dog(Animal):
    """狗类"""

    def speak(self) -> str:
        return "Woof!"


class Cat(Animal):
    """猫类"""

    def speak(self) -> str:
        return "Meow!"


# 使用
dog = Dog("Buddy")
print(dog.name)    # Buddy
print(dog.speak()) # Woof!

cat = Cat("Whiskers")
print(cat.speak())  # Meow!

# 多态
animals: list[Animal] = [Dog("Rex"), Cat("Luna"), Animal("Generic")]

for animal in animals:
    print(f"{animal.name}: {animal.speak()}")

10.3.2 super() 函数

使用 super() 调用父类的方法:

class Person:
    def __init__(self, name: str, age: int) -> None:
        self.name = name
        self.age = age


class Employee(Person):
    def __init__(self, name: str, age: int, employee_id: str) -> None:
        # 调用父类的 __init__
        super().__init__(name, age)
        self.employee_id = employee_id

    def __str__(self) -> str:
        return f"{super().__str__()}, ID: {self.employee_id}"


# 使用
emp = Employee("Alice", 30, "E001")
print(emp.name)        # Alice
print(emp.employee_id)  # E001
print(emp)             # Person(name='Alice', age=30), ID: E001

10.3.3 方法重写

子类可以重写父类的方法:

class Shape:
    def area(self) -> float:
        raise NotImplementedError("子类必须实现 area 方法")

    def perimeter(self) -> float:
        raise NotImplementedError("子类必须实现 perimeter 方法")


class Rectangle(Shape):
    def __init__(self, width: float, height: float) -> None:
        self.width = width
        self.height = height

    def area(self) -> float:
        return self.width * self.height

    def perimeter(self) -> float:
        return 2 * (self.width + self.height)


class Circle(Shape):
    def __init__(self, radius: float) -> None:
        self.radius = radius

    def area(self) -> float:
        import math
        return math.pi * self.radius ** 2

    def perimeter(self) -> float:
        import math
        return 2 * math.pi * self.radius

10.3.4 多重继承

Python 支持多重继承,但需要注意方法解析顺序(MRO):

class Flyable:
    def fly(self) -> str:
        return "Flying..."


class Swimmable:
    def swim(self) -> str:
        return "Swimming..."


class Duck(Animal, Flyable, Swimmable):
    def speak(self) -> str:
        return "Quack!"


# 使用
duck = Duck("Donald")
print(duck.fly())    # Flying...
print(duck.swim())   # Swimming...
print(duck.speak())  # Quack!

# 查看方法解析顺序
print(Duck.__mro__)

10.3.5 抽象基类

使用 abc 模块定义抽象基类:

from abc import ABC, abstractmethod


class Shape(ABC):
    """抽象图形类"""

    @abstractmethod
    def area(self) -> float:
        """计算面积"""
        pass

    @abstractmethod
    def perimeter(self) -> float:
        """计算周长"""
        pass

    # 可以有具体方法
    def describe(self) -> str:
        return f"A shape with area {self.area():.2f}"


class Rectangle(Shape):
    def __init__(self, width: float, height: float) -> None:
        self.width = width
        self.height = height

    def area(self) -> float:
        return self.width * self.height

    def perimeter(self) -> float:
        return 2 * (self.width + self.height)


# 不能直接实例化抽象类
# shape = Shape()  # TypeError

rect = Rectangle(5, 3)
print(rect.area())      # 15
print(rect.perimeter())  # 16
print(rect.describe())   # A shape with area 15.00

10.4 多态

10.4.1 什么是多态

多态允许不同类的对象对同一消息作出不同的响应。

# 函数使用多态
def make_speak(animal: Animal) -> str:
    return animal.speak()


dog = Dog("Rex")
cat = Cat("Luna")

print(make_speak(dog))  # Woof!
print(make_speak(cat))  # Meow!

10.4.2 运算符重载

通过特殊方法实现运算符重载:

class Vector:
    """二维向量"""

    def __init__(self, x: float, y: float) -> None:
        self.x = x
        self.y = y

    def __add__(self, other: "Vector") -> "Vector":
        return Vector(self.x + other.x, self.y + other.y)

    def __sub__(self, other: "Vector") -> "Vector":
        return Vector(self.x - other.x, self.y - other.y)

    def __mul__(self, scalar: float) -> "Vector":
        return Vector(self.x * scalar, self.y * scalar)

    def __rmul__(self, scalar: float) -> "Vector":
        return self.__mul__(scalar)

    def __neg__(self) -> "Vector":
        return Vector(-self.x, -self.y)

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Vector):
            return NotImplemented
        return self.x == other.x and self.y == other.y

    def __repr__(self) -> str:
        return f"Vector({self.x}, {self.y})"

    def __str__(self) -> str:
        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 * 3)    # (3, 6)
print(3 * v1)    # (3, 6)
print(-v1)       # (-1, -2)
print(v1 == Vector(1, 2))  # True

10.4.3 完整运算符列表

方法 运算符
__add__ +
__sub__ -
__mul__ *
__truediv__ /
__floordiv__ //
__mod__ %
__pow__ **
__and__ &
__or__ |
__xor__ ^
__lt__, __le__, __gt__, __ge__, __eq__, __ne__ < <= > >= == !=

10.5 dataclasses (Python 3.7+)

10.5.1 基本使用

dataclasses 模块可以自动生成 __init____repr____eq__ 等方法:

from dataclasses import dataclass


@dataclass
class Person:
    name: str
    age: int
    email: str = ""
    active: bool = True


# 自动生成 __init__, __repr__, __eq__
person = Person("Alice", 25)
print(person)  # Person(name='Alice', age=25, email='', active=True)

# 支持默认值
person2 = Person("Bob", 30, "bob@example.com")
print(person2)  # Person(name='Bob', age=30, email='bob@example.com', active=True)

# 比较
print(person == person2)  # False

10.5.2 字段默认值

from dataclasses import dataclass, field


@dataclass
class User:
    id: int
    name: str
    tags: list[str] = field(default_factory=list)  # 使用 factory
    created_at: str = field(default="")

    def __post_init__(self) -> None:
        """初始化后处理"""
        if not self.created_at:
            from datetime import datetime
            self.created_at = datetime.now().isoformat()


# 使用
user = User(1, "Alice")
print(user)  # User(id=1, name='Alice', tags=[], created_at='2024-01-15T10:30:00')

user2 = User(2, "Bob", tags=["admin", "user"])
print(user2)  # User(id=2, name='Bob', tags=['admin', 'user'], created_at='')

10.5.3 不可变 dataclass

from dataclasses import dataclass


@dataclass(frozen=True)
class Point:
    x: float
    y: float

    def distance_from_origin(self) -> float:
        return (self.x ** 2 + self.y ** 2) ** 0.5


# 不可变
p = Point(3, 4)
print(p)              # Point(x=3, y=4)
print(p.distance_from_origin())  # 5.0

# 尝试修改会报错
# p.x = 5  # FrozenInstanceError

10.5.4 dataclass 进阶

from dataclasses import dataclass, field
from typing import ClassVar


@dataclass
class Rectangle:
    width: float
    height: float

    # 类变量
    count: ClassVar[int] = 0

    def __post_init__(self) -> None:
        Rectangle.count += 1
        if self.width <= 0 or self.height <= 0:
            raise ValueError("尺寸必须为正数")

    @property
    def area(self) -> float:
        return self.width * self.height

    @property
    def perimeter(self) -> float:
        return 2 * (self.width + self.height)


# 使用
r1 = Rectangle(5, 3)
r2 = Rectangle(4, 2)

print(r1.area)       # 15
print(r2.perimeter)  # 12
print(Rectangle.count)  # 2

10.6 特殊方法

10.6.1 常用特殊方法

class MyList:
    """自定义列表类"""

    def __init__(self, items: list[int]) -> None:
        self.items = items

    def __len__(self) -> int:
        return len(self.items)

    def __getitem__(self, index: int) -> int:
        return self.items[index]

    def __setitem__(self, index: int, value: int) -> None:
        self.items[index] = value

    def __contains__(self, item: int) -> bool:
        return item in self.items

    def __iter__(self):
        return iter(self.items)

    def __reversed__(self):
        return reversed(self.items)

    def __add__(self, other: "MyList") -> "MyList":
        return MyList(self.items + other.items)

    def __str__(self) -> str:
        return f"MyList({self.items})"


# 使用
ml = MyList([1, 2, 3])
print(len(ml))       # 3
print(ml[0])         # 1
ml[0] = 10
print(ml)            # MyList([10, 2, 3])
print(1 in ml)       # False
print(2 in ml)       # True
print(list(ml))      # [10, 2, 3]
print(list(reversed(ml)))  # [3, 2, 10]

ml2 = MyList([4, 5])
ml3 = ml + ml2
print(ml3)           # MyList([10, 2, 3, 4, 5])

10.6.2 callable 对象

class Counter:
    """可调用对象"""

    def __init__(self, start: int = 0) -> None:
        self.count = start

    def __call__(self) -> int:
        self.count += 1
        return self.count


# 使用
counter = Counter()
print(counter())  # 1
print(counter())  # 2
print(counter())  # 3

10.7 综合示例

示例 1:银行账户系统

from dataclasses import dataclass, field
from datetime import datetime
from typing import ClassVar


class InsufficientFundsError(Exception):
    """余额不足异常"""
    pass


class InvalidAmountError(Exception):
    """无效金额异常"""
    pass


@dataclass
class BankAccount:
    """银行账户"""

    account_id: str
    holder_name: str
    _balance: float = 0.0
    _transaction_count: ClassVar[int] = 0

    def __post_init__(self) -> None:
        if self._balance < 0:
            raise ValueError("初始余额不能为负")

    @property
    def balance(self) -> float:
        return self._balance

    def deposit(self, amount: float) -> None:
        """存款"""
        if amount <= 0:
            raise InvalidAmountError("存款金额必须为正")
        self._balance += amount
        BankAccount._transaction_count += 1

    def withdraw(self, amount: float) -> None:
        """取款"""
        if amount <= 0:
            raise InvalidAmountError("取款金额必须为正")
        if amount > self._balance:
            raise InsufficientFundsError(
                f"余额不足: 余额 {self._balance}, 尝试取款 {amount}"
            )
        self._balance -= amount
        BankAccount._transaction_count += 1

    def transfer(self, target: "BankAccount", amount: float) -> None:
        """转账"""
        self.withdraw(amount)
        target.deposit(amount)

    def __str__(self) -> str:
        return f"Account({self.account_id}, {self.holder_name}, ${self._balance:.2f})"


# 使用
account1 = BankAccount("001", "Alice", 1000)
account2 = BankAccount("002", "Bob", 500)

print(account1)  # Account(001, Alice, $1000.00)

account1.deposit(500)
print(account1.balance)  # 1500

account1.withdraw(200)
print(account1.balance)  # 1300

account1.transfer(account2, 300)
print(account1.balance)  # 1000
print(account2.balance)  # 800

print(f"总交易次数: {BankAccount._transaction_count}")  # 5

# 异常处理
try:
    account1.withdraw(2000)
except InsufficientFundsError as e:
    print(f"错误: {e}")

示例 2:形状系统

from abc import ABC, abstractmethod
from dataclasses import dataclass
import math


class Shape(ABC):
    """抽象图形基类"""

    @abstractmethod
    def area(self) -> float:
        pass

    @abstractmethod
    def perimeter(self) -> float:
        pass

    def describe(self) -> str:
        return f"{self.__class__.__name__}: 面积={self.area():.2f}, 周长={self.perimeter():.2f}"


@dataclass
class Rectangle(Shape):
    width: float
    height: float

    def area(self) -> float:
        return self.width * self.height

    def perimeter(self) -> float:
        return 2 * (self.width + self.height)


@dataclass
class Circle(Shape):
    radius: float

    def area(self) -> float:
        return math.pi * self.radius ** 2

    def perimeter(self) -> float:
        return 2 * math.pi * self.radius


@dataclass
class Triangle(Shape):
    a: float
    b: float
    c: float

    def area(self) -> float:
        # 海伦公式
        s = self.perimeter() / 2
        return math.sqrt(s * (s - self.a) * (s - self.b) * (s - self.c))

    def perimeter(self) -> float:
        return self.a + self.b + self.c


# 使用
shapes: list[Shape] = [
    Rectangle(5, 3),
    Circle(2),
    Triangle(3, 4, 5)
]

total_area = 0.0
for shape in shapes:
    print(shape.describe())
    total_area += shape.area()

print(f"\n总面积: {total_area:.2f}")

最佳实践

  1. 优先使用 dataclasses:简化数据类的创建
# ✗ 不推荐
class Person:
    def __init__(self, name: str, age: int) -> None:
        self.name = name
        self.age = age

# ✓ 推荐
@dataclass
class Person:
    name: str
    age: int
  1. 使用抽象基类定义接口:明确类的职责

  2. 遵循单一职责原则:每个类只负责一件事

  3. 使用属性而不是直接访问:提供更好的封装

  4. 避免多重继承:增加复杂性

课后练习

练习 10.1:Rectangle 类

创建一个 Rectangle 类,包含:

  • width 和 height 属性
  • area() 和 perimeter() 方法
  • 使用 property 实现 getter/setter

练习 10.2:继承实现动物类

创建 Animal 基类和 Dog、Cat 子类,实现 speak() 方法。

练习 10.3:使用 dataclass

使用 dataclass 创建 Student 类,包含 id、name、age、grades 属性。

练习 10.4:运算符重载

创建一个复数类 Complex,支持 +、-、*、/ 运算。

练习 10.5:抽象基类

创建一个抽象基类 Stack,实现 push、pop、is_empty 方法。

练习 10.6:银行账户系统

创建一个完整的银行账户系统,支持存款、取款、转账、查询余额。

本章小结

本章我们详细学习了 Python 面向对象编程:

  1. 类和对象

    • 类的定义和实例化
    • 属性和方法
    • self 参数
  2. 封装

    • 访问控制(受保护、私有)
    • property 装饰器
  3. 继承

    • 单继承和多重继承
    • super() 函数
    • 方法重写
  4. 多态

    • 运算符重载
    • 抽象基类
  5. dataclasses

    • 简化数据类
    • 不可变 dataclass
  6. 特殊方法

    • 常用特殊方法
    • callable 对象

面向对象编程是 Python 开发大型应用的基础,需要多加练习。

相关资源


评论