Skip to content

章节4 面向对象编程

授课时长:约5-6学时
适用对象:已掌握函数和模块化编程的学员
教学方式:讲授 + 图表分析 + 代码实战


一、学习目标

目标分类具体目标
知识目标理解类与对象、三大特性(封装继承多态)、魔法方法、元类与设计模式
技能目标能设计类层次结构、重载运算符、使用反射动态操作对象、实现单例工厂模式
素养目标建立面向对象思维,设计高内聚低耦合的软件架构

二、核心知识点

1️⃣ 类与对象属性方法

1.1 类的定义与实例化

python
# 类的基本定义
class Student:
    """学生类"""
    # 类属性(所有实例共享)
    school = "Python大学"

    # 构造方法(实例化时自动调用)
    def __init__(self, name: str, age: int):
        # 实例属性(每个实例独有)
        self.name = name
        self.age = age

    # 实例方法
    def introduce(self) -> str:
        return f"我叫{self.name},今年{self.age}岁,来自{self.school}"


# 实例化
s1 = Student("小明", 20)
s2 = Student("小红", 22)

print(s1.introduce())       # 我叫小明,今年20岁,来自Python大学
print(s2.introduce())       # 我叫小红,今年22岁,来自Python大学

# 类属性可以通过类或实例访问
print(Student.school)       # Python大学
print(s1.school)            # Python大学

# 修改类属性
Student.school = "新Python大学"
print(s1.school)            # 新Python大学

1.2 self 参数详解

python
class Demo:
    def method(self):
        """self 是实例对象本身的引用"""
        print(f"self 的内存地址: {id(self)}")

d = Demo()
print(f"d    的内存地址: {id(d)}")
d.method()

# 等价调用方式
Demo.method(d)     # ✅ 底层就是这样调用的

1.3 类方法、静态方法与属性

python
class Circle:
    """圆形类"""
    pi = 3.14159  # 类属性

    def __init__(self, radius: float):
        self._radius = radius  # 私有属性(约定)

    # ──────────────────────────────────────
    # 实例方法(第一个参数是 self)
    def area(self) -> float:
        return self.pi * self._radius ** 2

    # ──────────────────────────────────────
    # 类方法(第一个参数是 cls)
    @classmethod
    def from_diameter(cls, diameter: float) -> "Circle":
        """通过直径创建 Circle 实例"""
        return cls(diameter / 2)

    @classmethod
    def update_pi(cls, new_pi: float):
        """更新圆周率"""
        cls.pi = new_pi

    # ──────────────────────────────────────
    # 静态方法(不需要 self 或 cls)
    @staticmethod
    def is_valid_radius(radius: float) -> bool:
        """验证半径是否合法"""
        return radius > 0

    # ──────────────────────────────────────
    # 属性(property):控制属性访问
    @property
    def radius(self) -> float:
        """getter"""
        return self._radius

    @radius.setter
    def radius(self, value: float):
        """setter:设置半径时自动验证"""
        if value <= 0:
            raise ValueError("半径必须大于0")
        self._radius = value

    @property
    def diameter(self) -> float:
        """只读属性(没有setter)"""
        return self._radius * 2


# 使用
c = Circle(5)
print(c.area())              # 78.53975
print(c.radius)              # 5(通过property访问)
c.radius = 10                # 通过setter修改
print(c.diameter)            # 20(只读属性)

# 类方法
c2 = Circle.from_diameter(20)
print(c2.radius)             # 10.0

# 静态方法
print(Circle.is_valid_radius(-1))  # False

1.4 访问控制

python
class BankAccount:
    """银行账户(演示访问控制)"""

    def __init__(self, owner: str, balance: float):
        self.owner = owner          # 公开属性
        self._branch = "总行"       # 保护属性(单下划线,约定为内部使用)
        self.__password = "123456"  # 私有属性(双下划线,名称改写)

    def __str__(self):
        return f"{self.owner}的账户"

    # 获取私有属性的方法
    def get_password(self) -> str:
        return self.__password

    # 修改私有属性的方法
    def set_password(self, new_pwd: str):
        if len(new_pwd) >= 6:
            self.__password = new_pwd
        else:
            raise ValueError("密码长度至少6位")


acc = BankAccount("张三", 10000)
print(acc.owner)              # 张三(公开)
print(acc._branch)            # 总行(保护,Python不强制)
# print(acc.__password)       # ❌ AttributeError

# 名称改写机制
print(acc._BankAccount__password)  # 123456(可通过此方式访问,但不推荐)

# 推荐通过方法访问
print(acc.get_password())     # 123456

2️⃣ 封装继承多态三大特性

2.1 封装(Encapsulation)

python
"""
封装:将数据和操作数据的方法绑定在一起,对外隐藏内部实现细节。
通过 property 提供受控的访问接口。
"""

class Temperature:
    """温度类:内部使用摄氏度,对外提供华氏度转换"""
    def __init__(self, celsius: float = 0):
        self._celsius = celsius

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

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

    @property
    def fahrenheit(self) -> float:
        """华氏度(只读,通过摄氏度计算)"""
        return self._celsius * 9 / 5 + 32

    def __str__(self):
        return f"{self._celsius}°C / {self.fahrenheit}°F"


t = Temperature(25)
print(t.celsius)       # 25
print(t.fahrenheit)    # 77.0
t.celsius = 30
print(t)               # 30°C / 86.0°F

2.2 继承(Inheritance)

python
# ──────────────────────────────────────────
# 基本继承
class Animal:
    """动物基类"""
    def __init__(self, name: str):
        self.name = name

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


class Dog(Animal):
    """狗类(继承Animal)"""
    def speak(self) -> str:
        return f"{self.name}说:汪汪!"


class Cat(Animal):
    """猫类(继承Animal)"""
    def speak(self) -> str:
        return f"{self.name}说:喵喵!"


# 使用
dog = Dog("旺财")
cat = Cat("咪咪")
print(dog.speak())       # 旺财说:汪汪!
print(cat.speak())       # 咪咪说:喵喵!

# ──────────────────────────────────────────
# super() —— 调用父类方法
class Vehicle:
    def __init__(self, brand: str, model: str):
        self.brand = brand
        self.model = model

    def info(self) -> str:
        return f"{self.brand} {self.model}"


class Car(Vehicle):
    def __init__(self, brand: str, model: str, doors: int):
        super().__init__(brand, model)   # 调用父类构造
        self.doors = doors

    def info(self) -> str:
        base_info = super().info()       # 复用父类方法
        return f"{base_info} ({self.doors}门)"


car = Car("Toyota", "Camry", 4)
print(car.info())        # Toyota Camry (4门)

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

    def __str__(self) -> str:
        return f"形状(面积={self.area():.2f})"


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

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


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

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


shapes = [Rectangle(3, 4), Circle(5)]
for s in shapes:
    print(s)  # 形状(面积=12.00)  形状(面积=78.54)

2.3 多继承与MRO

python
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):   # D 同时继承 B 和 C
    pass

d = D()
print(d.method())          # B(MRO:D → B → C → A)
print(D.__mro__)           # 查看方法解析顺序

# MRO(Method Resolution Order)使用 C3 线性化算法

2.4 多态(Polymorphism)

python
"""
多态:同一个接口,不同的实现。
Python 的"鸭子类型"(Duck Typing)—— 如果它走起来像鸭子,叫起来像鸭子,它就是鸭子。
"""

# ──────────────────────────────────────────
# 方式1:继承中的多态(已在上文演示)
# ──────────────────────────────────────────
# 方式2:鸭子类型

class Duck:
    def sound(self):
        return "嘎嘎嘎"

class Dog:
    def sound(self):
        return "汪汪汪"

class Cat:
    def sound(self):
        return "喵喵喵"

def make_sound(animal):
    """不用关心 animal 是什么类型,只要它有 sound 方法"""
    return animal.sound()

# 这些类没有继承关系,但可以多态使用
animals = [Duck(), Dog(), Cat()]
for a in animals:
    print(make_sound(a))

# ──────────────────────────────────────────
# 方式3:抽象基类(ABC)
from abc import ABC, abstractmethod

class Payment(ABC):
    """支付抽象基类"""

    @abstractmethod
    def pay(self, amount: float) -> bool:
        """支付方法(子类必须实现)"""
        pass

class Alipay(Payment):
    def pay(self, amount: float) -> bool:
        print(f"支付宝支付 {amount} 元")
        return True

class WechatPay(Payment):
    def pay(self, amount: float) -> bool:
        print(f"微信支付 {amount} 元")
        return True

# p = Payment()          # ❌ TypeError: 不能实例化抽象类
alipay = Alipay()
wechat = WechatPay()

def process_payment(payment: Payment, amount: float):
    if payment.pay(amount):
        print("支付成功")
    else:
        print("支付失败")

process_payment(alipay, 100)
process_payment(wechat, 200)

3️⃣ 魔法方法与运算符重载

3.1 常用魔法方法总览

python
"""
魔法方法(Magic Methods / Dunder Methods):
以双下划线开头和结尾的特殊方法,Python 在特定场景自动调用。
"""

class Book:
    """图书类 —— 展示常用魔法方法"""

    # ──────────────────────────────────────
    # 构造与析构
    def __init__(self, title: str, author: str, pages: int):
        """构造方法:创建对象时自动调用"""
        self.title = title
        self.author = author
        self.pages = pages
        print(f"📚 创建图书: {self.title}")

    def __del__(self):
        """析构方法:对象被销毁时调用"""
        print(f"🗑️ 销毁图书: {self.title}")

    # ──────────────────────────────────────
    # 字符串表示
    def __str__(self) -> str:
        """用户可读的字符串表示(给用户看)"""
        return f"《{self.title}》- {self.author}"

    def __repr__(self) -> str:
        """官方字符串表示(给开发者看,可重现对象)"""
        return f"Book(title='{self.title}', author='{self.author}', pages={self.pages})"

    # ──────────────────────────────────────
    # 容器方法
    def __len__(self) -> int:
        """len() 函数支持"""
        return self.pages

    def __bool__(self) -> bool:
        """bool() 函数支持"""
        return self.pages > 0  # 0页的书视为假

    # ──────────────────────────────────────
    # 可调用对象
    def __call__(self, reader: str) -> str:
        """使对象可像函数一样调用"""
        return f"{reader} 正在阅读《{self.title}》"

    # ──────────────────────────────────────
    # 属性访问控制
    def __getattr__(self, name):
        """访问不存在的属性时调用"""
        return f"属性 '{name}' 不存在"

    def __setattr__(self, name, value):
        """设置属性时调用"""
        print(f"设置 {name} = {value}")
        super().__setattr__(name, value)


# 测试
book = Book("Python编程", "张三", 500)

print(str(book))            # 《Python编程》- 张三
print(repr(book))           # Book(title='Python编程', ...)
print(len(book))            # 500
print(bool(book))           # True
print(book("小明"))         # 小明 正在阅读《Python编程》
print(book.isbn)            # 属性 'isbn' 不存在

3.2 运算符重载

python
class Vector:
    """二维向量 —— 演示运算符重载"""

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

    # ──────────────────────────────────────
    # 算术运算符
    def __add__(self, other: "Vector") -> "Vector":
        """向量加法:v1 + v2"""
        return Vector(self.x + other.x, self.y + other.y)

    def __sub__(self, other: "Vector") -> "Vector":
        """向量减法:v1 - v2"""
        return Vector(self.x - other.x, self.y - other.y)

    def __mul__(self, scalar: float) -> "Vector":
        """标量乘法:v * n"""
        return Vector(self.x * scalar, self.y * scalar)

    def __rmul__(self, scalar: float) -> "Vector":
        """右乘法:n * v(当左边不支持时调用)"""
        return self.__mul__(scalar)

    def __truediv__(self, scalar: float) -> "Vector":
        """除法:v / n"""
        if scalar == 0:
            raise ZeroDivisionError("不能除以0")
        return Vector(self.x / scalar, self.y / scalar)

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

    # ──────────────────────────────────────
    # 比较运算符
    def __eq__(self, other: "Vector") -> bool:
        """相等判断:v1 == v2"""
        return self.x == other.x and self.y == other.y

    def __lt__(self, other: "Vector") -> bool:
        """小于判断:v1 < v2(按模长比较)"""
        return abs(self) < abs(other)

    def __abs__(self) -> float:
        """绝对值 / 模长:abs(v)"""
        return (self.x ** 2 + self.y ** 2) ** 0.5

    # ──────────────────────────────────────
    # 字符串表示
    def __str__(self) -> str:
        return f"Vector({self.x}, {self.y})"

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


# 测试
v1 = Vector(2, 3)
v2 = Vector(1, 2)

print(v1 + v2)           # Vector(3, 5)
print(v1 - v2)           # Vector(1, 1)
print(v1 * 3)            # Vector(6, 9)
print(3 * v1)            # Vector(6, 9)(右乘法)
print(-v1)               # Vector(-2, -3)
print(v1 == Vector(2, 3))  # True
print(v1 < v2)           # False(|v1| ≈ 3.6, |v2| ≈ 2.2)
print(abs(v1))           # 3.605551275463989

3.3 上下文管理协议

python
# 使用 __enter__ 和 __exit__ 实现上下文管理器
class ManagedFile:
    """自定义文件上下文管理器"""

    def __init__(self, filename: str, mode: str = "r"):
        self.filename = filename
        self.mode = mode

    def __enter__(self):
        """with 块开始时调用"""
        self.file = open(self.filename, self.mode, encoding="utf-8")
        print(f"📂 打开文件: {self.filename}")
        return self.file  # 返回给 as 子句

    def __exit__(self, exc_type, exc_val, exc_tb):
        """with 块结束时调用(无论是否异常)"""
        self.file.close()
        print(f"📂 关闭文件: {self.filename}")
        # 返回 False 则异常会向外传递,返回 True 则异常被吞掉
        return False

# 使用
with ManagedFile("test.txt", "w") as f:
    f.write("Hello, World!")

4️⃣ 元类反射与动态属性

4.1 元类(Metaclass)

python
"""
元类是"创建类的类"。
类是元类的实例。默认元类是 type。
"""

# 类也是对象
class MyClass:
    pass

print(type(MyClass))       # <class 'type'>
print(type(type))          # <class 'type'>(type的元类是它自己)

# 使用 type 动态创建类
# type(类名, 父类元组, 属性字典)
Student = type("Student", (), {
    "school": "Python大学",
    "__init__": lambda self, name: setattr(self, "name", name),
    "intro": lambda self: f"我叫{self.name}"
})

s = Student("小明")
print(s.intro())           # 我叫小明

# ──────────────────────────────────────────
# 自定义元类
class SingletonMeta(type):
    """单例元类:确保类只有一个实例"""
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

# 使用元类
class Database(metaclass=SingletonMeta):
    def __init__(self):
        print("数据库连接创建")

db1 = Database()   # 数据库连接创建
db2 = Database()   # 不打印(复用实例)
print(db1 is db2)  # True

4.2 反射(Reflection)

python
"""
反射:运行时动态获取对象的类型、属性和方法。
"""

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

    def say_hello(self):
        return f"{self.name} 说你好"


p = Person("张三", 25)

# ──────────────────────────────────────────
# hasattr() — 是否有某个属性
print(hasattr(p, "name"))        # True
print(hasattr(p, "gender"))      # False

# ──────────────────────────────────────────
# getattr() — 获取属性
name = getattr(p, "name")
print(name)                      # 张三

# 带默认值
gender = getattr(p, "gender", "未知")
print(gender)                    # 未知

# ──────────────────────────────────────────
# setattr() — 设置属性
setattr(p, "age", 26)
setattr(p, "gender", "男")
print(p.age)                     # 26
print(p.gender)                  # 男

# ──────────────────────────────────────────
# delattr() — 删除属性
# delattr(p, "age")

# ──────────────────────────────────────────
# callable() — 判断是否可调用
method = getattr(p, "say_hello")
if callable(method):
    print(method())              # 张三 说你好

# ──────────────────────────────────────────
# 实际应用:动态调用API
class APIClient:
    def get_user(self, user_id):
        return {"id": user_id, "name": "Alice"}

    def get_order(self, order_id):
        return {"id": order_id, "amount": 100}

    def create_user(self, name, email):
        return {"status": "ok", "name": name}

def call_api(client, method_name, **params):
    """动态调用API方法"""
    if not hasattr(client, method_name):
        raise ValueError(f"API方法 {method_name} 不存在")

    method = getattr(client, method_name)

    if not callable(method):
        raise ValueError(f"{method_name} 不可调用")

    return method(**params)


client = APIClient()
result = call_api(client, "get_user", user_id=1)
print(result)  # {'id': 1, 'name': 'Alice'}

4.3 动态属性与 __getattr__

python
# 动态属性代理
class DynamicConfig:
    """从字典动态读取配置"""

    def __init__(self, config: dict):
        self._config = config

    def __getattr__(self, name):
        """访问不存在的属性时自动查找配置"""
        if name.startswith("_"):
            raise AttributeError(name)
        if name in self._config:
            return self._config[name]
        raise AttributeError(f"配置项 '{name}' 不存在")

    def __setattr__(self, name, value):
        """设置属性"""
        if name.startswith("_"):
            super().__setattr__(name, value)
        else:
            self._config[name] = value

    def __repr__(self):
        return f"DynamicConfig({self._config})"


config_data = {
    "host": "localhost",
    "port": 8080,
    "debug": True,
}

config = DynamicConfig(config_data)
print(config.host)         # localhost
print(config.port)         # 8080
config.debug = False       # 动态修改配置

5️⃣ 单例工厂等设计模式

5.1 单例模式(Singleton)

python
"""
保证一个类只有一个实例,并提供一个全局访问点。
常用于:数据库连接、日志记录器、配置管理器。
"""

# ──────────────────────────────────────────
# 方式1:使用模块(Python 模块天然是单例)
# config.py
# DATABASE = {"host": "localhost", "port": 5432}
# 使用时:from config import DATABASE

# ──────────────────────────────────────────
# 方式2:使用元类(已在上文演示)

# ──────────────────────────────────────────
# 方式3:使用装饰器
def singleton(cls):
    """单例装饰器"""
    instances = {}
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return get_instance

@singleton
class Logger:
    def __init__(self):
        self.logs = []

    def log(self, message: str):
        self.logs.append(message)
        print(f"[LOG] {message}")

# 测试
logger1 = Logger()
logger2 = Logger()
print(logger1 is logger2)  # True

5.2 工厂模式(Factory)

python
"""
定义一个创建对象的接口,让子类决定实例化哪个类。
"""

# ──────────────────────────────────────────
# 简单工厂
from abc import ABC, abstractmethod

class Notification(ABC):
    @abstractmethod
    def send(self, message: str) -> bool:
        pass

class EmailNotification(Notification):
    def send(self, message: str) -> bool:
        print(f"[邮件] 发送: {message}")
        return True

class SMSNotification(Notification):
    def send(self, message: str) -> bool:
        print(f"[短信] 发送: {message}")
        return True

class WeChatNotification(Notification):
    def send(self, message: str) -> bool:
        print(f"[微信] 发送: {message}")
        return True

class NotificationFactory:
    """通知工厂"""
    @staticmethod
    def create(notification_type: str) -> Notification:
        factories = {
            "email": EmailNotification,
            "sms": SMSNotification,
            "wechat": WeChatNotification,
        }
        cls = factories.get(notification_type.lower())
        if not cls:
            raise ValueError(f"不支持的通知类型: {notification_type}")
        return cls()

# 使用
notifier = NotificationFactory.create("email")
notifier.send("你好!")

# ──────────────────────────────────────────
# 工厂方法模式
class FileExporter(ABC):
    @abstractmethod
    def export(self, data: list) -> str:
        pass

class CSVExporter(FileExporter):
    def export(self, data: list) -> str:
        lines = [",".join(map(str, item)) for item in data]
        return "\n".join(lines)

class JSONExporter(FileExporter):
    def export(self, data: list) -> str:
        import json
        return json.dumps(data, ensure_ascii=False, indent=2)

class ExporterFactory(ABC):
    @abstractmethod
    def create_exporter(self) -> FileExporter:
        pass

class CSVExporterFactory(ExporterFactory):
    def create_exporter(self) -> FileExporter:
        return CSVExporter()

class JSONExporterFactory(ExporterFactory):
    def create_exporter(self) -> FileExporter:
        return JSONExporter()

# 使用
def export_data(factory: ExporterFactory, data: list):
    exporter = factory.create_exporter()
    return exporter.export(data)

data = [("张三", 25), ("李四", 30)]
csv_factory = CSVExporterFactory()
print(export_data(csv_factory, data))

5.3 建造者模式(Builder)

python
"""
分离复杂对象的构建与表示,同样的构建过程可以创建不同的表示。
"""

class Computer:
    """计算机产品"""

    def __init__(self):
        self.cpu = None
        self.gpu = None
        self.ram = None
        self.storage = None
        self.os = None

    def __str__(self):
        return (f"电脑配置: CPU={self.cpu}, GPU={self.gpu}, "
                f"内存={self.ram}, 存储={self.storage}, 系统={self.os}")


class ComputerBuilder:
    """计算机建造者"""

    def __init__(self):
        self.computer = Computer()

    def set_cpu(self, cpu: str) -> "ComputerBuilder":
        self.computer.cpu = cpu
        return self

    def set_gpu(self, gpu: str) -> "ComputerBuilder":
        self.computer.gpu = gpu
        return self

    def set_ram(self, ram: str) -> "ComputerBuilder":
        self.computer.ram = ram
        return self

    def set_storage(self, storage: str) -> "ComputerBuilder":
        self.computer.storage = storage
        return self

    def set_os(self, os: str) -> "ComputerBuilder":
        self.computer.os = os
        return self

    def build(self) -> Computer:
        return self.computer


# 使用
office_pc = (ComputerBuilder()
             .set_cpu("Intel i5")
             .set_ram("16GB")
             .set_storage("512GB SSD")
             .set_os("Windows 11")
             .build())

gaming_pc = (ComputerBuilder()
             .set_cpu("AMD Ryzen 7")
             .set_gpu("RTX 4070")
             .set_ram("32GB")
             .set_storage("1TB NVMe")
             .set_os("Windows 11")
             .build())

print(office_pc)
print(gaming_pc)

5.4 策略模式(Strategy)

python
"""
定义一系列算法,把它们封装起来,并使它们可以互相替换。
"""

from abc import ABC, abstractmethod

class DiscountStrategy(ABC):
    """折扣策略接口"""
    @abstractmethod
    def calculate(self, total: float) -> float:
        pass

class NoDiscount(DiscountStrategy):
    def calculate(self, total: float) -> float:
        return total

class PercentageDiscount(DiscountStrategy):
    def __init__(self, percent: float):
        self.percent = percent

    def calculate(self, total: float) -> float:
        return total * (1 - self.percent / 100)

class ThresholdDiscount(DiscountStrategy):
    def __init__(self, threshold: float, discount: float):
        self.threshold = threshold
        self.discount = discount

    def calculate(self, total: float) -> float:
        if total >= self.threshold:
            return total - self.discount
        return total

class Order:
    """订单类,使用策略模式计算折扣"""

    def __init__(self, total: float, strategy: DiscountStrategy):
        self.total = total
        self.strategy = strategy

    def final_price(self) -> float:
        return self.strategy.calculate(self.total)


# 使用
order1 = Order(1000, NoDiscount())
print(f"无折扣: {order1.final_price()}")        # 1000

order2 = Order(1000, PercentageDiscount(20))
print(f"八折: {order2.final_price()}")           # 800

order3 = Order(1000, ThresholdDiscount(500, 100))
print(f"满500减100: {order3.final_price()}")     # 900

三、代码实操演示

演示:综合案例 — 图书管理系统

python
"""图书管理系统(演示OOP核心特性)"""
from abc import ABC, abstractmethod
from datetime import datetime, timedelta
from typing import List, Optional


class Book:
    """图书类"""
    def __init__(self, isbn: str, title: str, author: str, year: int):
        self.isbn = isbn
        self.title = title
        self.author = author
        self.year = year
        self._is_borrowed = False

    @property
    def is_borrowed(self) -> bool:
        return self._is_borrowed

    def borrow(self) -> bool:
        if self._is_borrowed:
            return False
        self._is_borrowed = True
        return True

    def return_book(self):
        self._is_borrowed = False

    def __str__(self) -> str:
        status = "已借出" if self._is_borrowed else "可借阅"
        return f"《{self.title}{self.author}({self.year}) [{status}]"


class Member(ABC):
    """会员抽象基类"""
    def __init__(self, name: str, member_id: str):
        self.name = name
        self.member_id = member_id
        self.borrowed_books: List[Book] = []

    @abstractmethod
    def max_books(self) -> int:
        pass

    @abstractmethod
    def borrow_duration(self) -> int:
        """借阅天数"""
        pass

    def can_borrow(self) -> bool:
        return len(self.borrowed_books) < self.max_books()

    def borrow(self, book: Book) -> bool:
        if not self.can_borrow():
            print(f"{self.name}已达借阅上限")
            return False
        if not book.borrow():
            print(f"《{book.title}》已被借出")
            return False
        self.borrowed_books.append(book)
        print(f"{self.name}借阅《{book.title}》成功")
        return True

    def return_book(self, book: Book):
        if book in self.borrowed_books:
            book.return_book()
            self.borrowed_books.remove(book)
            print(f"{self.name}归还《{book.title}》成功")
        else:
            print(f"{self.name}未借阅《{book.title}》")

    def __str__(self) -> str:
        return f"{self.name}({self.member_id}) - 已借{len(self.borrowed_books)}本"


class StudentMember(Member):
    """学生会员"""
    def max_books(self) -> int:
        return 3

    def borrow_duration(self) -> int:
        return 30


class TeacherMember(Member):
    """教师会员"""
    def max_books(self) -> int:
        return 10

    def borrow_duration(self) -> int:
        return 90


class Library:
    """图书馆类"""
    def __init__(self, name: str):
        self.name = name
        self.books: List[Book] = []
        self.members: List[Member] = []

    def add_book(self, book: Book):
        self.books.append(book)
        print(f"添加图书: {book}")

    def register_member(self, member: Member):
        self.members.append(member)
        print(f"注册会员: {member}")

    def search_book(self, keyword: str) -> List[Book]:
        results = []
        for book in self.books:
            if (keyword.lower() in book.title.lower()
                    or keyword.lower() in book.author.lower()):
                results.append(book)
        return results

    def list_available_books(self) -> List[Book]:
        return [b for b in self.books if not b.is_borrowed]


# 测试使用
lib = Library("Python社区图书馆")

# 添加图书
lib.add_book(Book("978-1", "Python编程从入门到实践", "Eric Matthes", 2019))
lib.add_book(Book("978-2", "流畅的Python", "Luciano Ramalho", 2017))
lib.add_book(Book("978-3", "Python核心编程", "Wesley Chun", 2016))
lib.add_book(Book("978-4", "数据结构与算法", "严蔚敏", 2018))

# 注册会员
alice = StudentMember("Alice", "S001")
bob = TeacherMember("Bob", "T001")
lib.register_member(alice)
lib.register_member(bob)

# 借书操作
books = lib.search_book("Python")
if books:
    alice.borrow(books[0])
    bob.borrow(books[1])

# 查看可借阅图书
print("\n当前可借阅图书:")
for book in lib.list_available_books():
    print(f"  {book}")

# 归还
alice.return_book(books[0])

四、常见错误与排错

错误类型示例原因与解决
TypeError: missing 1 required positional argumentDog.speak() 而不是 dog.speak()实例方法需要用实例调用,而不是类
AttributeError: '...' object has no attribute '...'访问了不存在的属性检查属性名是否正确,__init__中是否已定义
TypeError: cannot instantiate abstract class实例化了抽象类子类未实现所有抽象方法,检查 @abstractmethod
RecursionError__getattr__ 中递归调用__getattr__ 内部不要用 self.xxx(触发递归),用 self.__dict__
AttributeError: property '...' has no setter尝试给只读属性赋值property 只有 getter 没有 setter 时不能赋值
TypeError: init() should return None__init__ 返回了非 None__init__ 不允许有返回值

五、小结

知识图谱

面向对象编程
├── 类与对象
│   ├── 实例方法/类方法(@classmethod)/静态方法(@staticmethod)
│   ├── property 属性(getter/setter)
│   └── 访问控制(_保护/__私有)
├── 三大特性
│   ├── 封装:数据+方法绑定,隐藏实现细节
│   ├── 继承:代码复用,super()调用父类
│   └── 多态:同一个接口多种实现(鸭子类型)
├── 魔法方法
│   ├── __init__/__str__/__repr__/__len__
│   ├── 运算符重载:__add__/__eq__/__neg__
│   └── 上下文管理:__enter__/__exit__
├── 元类与反射
│   ├── type 动态创建类
│   ├── hasattr/getattr/setattr
│   └── 元类控制类的创建行为
└── 设计模式
    ├── 单例:全局唯一实例
    ├── 工厂:创建对象
    ├── 建造者:分步构建复杂对象
    └── 策略:算法互换

重点回顾

  1. 封装是OOP的基石——把数据和操作放在一起,对外提供简洁接口
  2. 继承要遵循"is-a"关系,组合优于继承(组合实现"has-a")
  3. Python的多态本质是鸭子类型——不关心对象是什么类型,只关心它有什么方法
  4. 魔法方法让自定义类可以和Python内置语法无缝对接
  5. 设计模式是经过验证的解决方案,但不要过度设计

六、课后练习

基础题

  1. 类定义:设计一个 BankAccount 类,包含账户名、余额、利率等属性,实现存款、取款、计算利息方法。
  2. 继承:基于 Shape 基类(含 area() 抽象方法),实现 RectangleCircleTriangle 子类。
  3. 魔法方法:为一个 Student 类实现 __str____repr____eq__(按学号判断相等)、__lt__(按成绩比较)。

进阶题

  1. property 应用:设计一个 Person 类,其中 age 属性使用 property 控制,设置时验证年龄在 0-150 范围内,且出生年份自动更新。
  2. 运算符重载:实现一个 Matrix 类(二维矩阵),支持 +(矩阵加法)、*(矩阵乘法)、==(相等判断)、__getitem__(索引访问)。
  3. 反射应用:实现一个通用对象转字典函数 object_to_dict(obj),能够递归地将任意 Python 对象的所有属性转换为字典格式。

挑战题

  1. 插件系统:使用元类和反射实现一个插件系统,插件通过继承基类并实现特定方法自动注册,主程序动态发现并加载所有插件。
  2. 简易 ORM 框架:实现一个极简的 ORM(对象关系映射)框架,支持通过类定义映射到数据库表,支持 save/find/delete 操作。使用元类在类定义时自动生成 SQL 语句。

参考资料

Python 学习资料