Appearance
章节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)) # False1.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()) # 1234562️⃣ 封装继承多态三大特性
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°F2.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.6055512754639893.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) # True4.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) # True5.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 argument | Dog.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
│ └── 元类控制类的创建行为
└── 设计模式
├── 单例:全局唯一实例
├── 工厂:创建对象
├── 建造者:分步构建复杂对象
└── 策略:算法互换重点回顾
- 封装是OOP的基石——把数据和操作放在一起,对外提供简洁接口
- 继承要遵循"is-a"关系,组合优于继承(组合实现"has-a")
- Python的多态本质是鸭子类型——不关心对象是什么类型,只关心它有什么方法
- 魔法方法让自定义类可以和Python内置语法无缝对接
- 设计模式是经过验证的解决方案,但不要过度设计
六、课后练习
基础题
- 类定义:设计一个
BankAccount类,包含账户名、余额、利率等属性,实现存款、取款、计算利息方法。 - 继承:基于
Shape基类(含area()抽象方法),实现Rectangle、Circle、Triangle子类。 - 魔法方法:为一个
Student类实现__str__、__repr__、__eq__(按学号判断相等)、__lt__(按成绩比较)。
进阶题
- property 应用:设计一个
Person类,其中age属性使用 property 控制,设置时验证年龄在 0-150 范围内,且出生年份自动更新。 - 运算符重载:实现一个
Matrix类(二维矩阵),支持+(矩阵加法)、*(矩阵乘法)、==(相等判断)、__getitem__(索引访问)。 - 反射应用:实现一个通用对象转字典函数
object_to_dict(obj),能够递归地将任意 Python 对象的所有属性转换为字典格式。
挑战题
- 插件系统:使用元类和反射实现一个插件系统,插件通过继承基类并实现特定方法自动注册,主程序动态发现并加载所有插件。
- 简易 ORM 框架:实现一个极简的 ORM(对象关系映射)框架,支持通过类定义映射到数据库表,支持 save/find/delete 操作。使用元类在类定义时自动生成 SQL 语句。
参考资料
- Python 官方文档-类:https://docs.python.org/zh-cn/3/tutorial/classes.html
- Python 数据模型:https://docs.python.org/zh-cn/3/reference/datamodel.html
- 设计模式:Python 版:https://github.com/faif/python-patterns
- Real Python OOP 系列:https://realpython.com/python3-object-oriented-programming/