07 面向对象编程
📋 资料说明
- 来源:课程 day06 后半 ~ day07
- 笔记:
尚硅谷大模型技术之Python1.0.docx第 8 章"面向对象" - 代码:
day06/代码/P03_PP_OOP_FP.py、P04_Def_Class.py~P09_Other_Method.py - 视频:❌ 跳过(约 29 个视频:OOP 概念 → 类定义 → 封装 → 继承 → 多态 → 愤怒的小鸟案例)
🟢 🤖 AI 替代率:89%
为什么是这个水平:
类定义、继承语法、方法重写——这些是确定性语法,AI 近乎完美。但 OOP 的核心不是"怎么写",而是"为什么这样设计":
- 封装:什么时候隐藏属性、什么时候暴露接口——需要判断
- 继承 vs 组合:过深的继承树会带来维护噩梦,但"多深算深"需要经验
- 多态:Python 的"鸭子类型"对刚从 Java 转来的人是个认知跃迁
这些设计决策是 AI 的薄弱环节。AI 可以写出符合语法规范的代码,但可能写出 5 层继承的"面条代码"。
👤 人工干预率:11% | 人类重点关注
为什么需要人工干预:
- 设计原则:单一职责(一个类只做一件事)、开闭原则(对扩展开放、对修改关闭)——这些需要人判断
- 继承 vs 组合:Python 支持多继承,但 MRO(方法解析顺序)容易混淆,过度使用继承应避免
- 鸭子类型:"看起来像鸭子、叫起来像鸭子,它就是鸭子"——Python 不检查类型,只检查方法
人类与 AI 协作建议:
- 让 AI 写出基础类结构,你来判断设计的合理性
- 重点关注:MRO 的执行顺序、区分
@classmethod/@staticmethod/ 实例方法的使用场景 - 遇到"这个类该不该拆"的问题时,先问 AI 的建议,再结合业务场景判断
1. 面向对象基本概念
| 概念 | 说明 | 类比 |
|---|---|---|
| 类 (Class) | 对大量对象共性的抽象 | 建筑设计图 |
| 对象 (Object) | 类的一个具体实例 | 按图纸建造的房子 |
| 属性 (Attribute) | 对象有什么 | 颜色、大小 |
| 方法 (Method) | 对象能做什么 | 跑步、唱歌 |
python
# 万物皆对象:在 Python 中,整数、字符串、函数……都是对象
# 类是对大量对象共性的抽象
# 在程序中,类是创建对象的模板
# 类是客观事物在人脑中的主观反映2. 类的定义与使用
python
class Person:
"""人的类"""
# 类属性(所有实例共享)
home = "Earth"
def __init__(self, name="", age=0):
# 实例属性(每个实例独享)
self.name = name
self.age = age
# 实例方法
def eat(self):
print(f"{self.name} is eating")
def drink(self):
print(f"{self.name} is drinking")
# 类的操作 - 成员引用
print(Person.home) # Earth(通过类名访问)
print(Person.__doc__) # 人的类
# 类的操作 - 实例化
p1 = Person("Alice", 25)
p2 = Person("Bob", 30)
print(p1.name) # Alice
print(p2.name) # Bob
p1.eat() # Alice is eating
p2.drink() # Bob is drinking3. 对象的创建过程
python
class Student:
def __new__(cls, *args, **kwargs):
"""
__new__ 才是真正的"构造方法"
负责创建实例并分配内存
大多数情况不需要重写
"""
print("1. 创建实例")
instance = super().__new__(cls)
return instance
def __init__(self, name, age):
"""
__init__ 是"初始化方法"
在 __new__ 创建实例后被调用
给实例的属性赋值
"""
print("2. 初始化实例")
self.name = name
self.age = age
s = Student("Alice", 25)
# 输出:
# 1. 创建实例
# 2. 初始化实例self 的作用
python
class Dog:
def __init__(self, name):
self.name = name
def bark(self):
# self 就是调用这个方法的实例对象
print(f"{self.name} says Wang!")
dog = Dog("旺财")
dog.bark() # 等价于 Dog.bark(dog)4. 类属性与实例属性
python
class Employee:
# 类属性(所有实例共享)
company = "ABC Corp"
employee_count = 0
def __init__(self, name, salary):
# 实例属性(每个实例独享)
self.name = name
self.salary = salary
Employee.employee_count += 1
e1 = Employee("Alice", 10000)
e2 = Employee("Bob", 12000)
print(e1.company) # ABC Corp(从类继承)
print(e2.company) # ABC Corp
# 修改类属性
Employee.company = "XYZ Corp"
print(e1.company) # XYZ Corp(所有实例同步变)
# 通过实例修改类属性——实际上是创建了同名的实例属性
e1.company = "My Corp" # 不是修改类属性!
print(e1.company) # My Corp(实例属性)
print(e2.company) # XYZ Corp(类属性没变)
print(Employee.company) # XYZ Corp(类属性没变)5. 方法类型
python
class MyClass:
# 实例方法:第一个参数是 self
def instance_method(self):
return f"实例方法,self={self}"
# 类方法:第一个参数是 cls
@classmethod
def class_method(cls):
return f"类方法,cls={cls}"
# 静态方法:没有 self 或 cls
@staticmethod
def static_method():
return "静态方法,无特殊参数"
# 魔法方法
def __str__(self):
return "MyClass 的实例"
def __len__(self):
return 42
obj = MyClass()
print(obj.instance_method()) # 实例方法,self=<__main__.MyClass object>
print(MyClass.class_method()) # 类方法,cls=<class '__main__.MyClass'>
print(MyClass.static_method())# 静态方法,无特殊参数
print(obj) # MyClass 的实例(调用了 __str__)6. 封装
封装:隐藏内部实现细节,只暴露必要的接口。
python
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self.__balance = balance # 私有属性(双下划线)
# 公开方法——对外接口
def deposit(self, amount):
if amount > 0:
self.__balance += amount
return True
return False
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
return True
return False
def get_balance(self):
return self.__balance
# @property:把方法变成属性访问
@property
def balance(self):
return self.__balance
@balance.setter
def balance(self, value):
if value >= 0:
self.__balance = value
else:
raise ValueError("余额不能为负数")
account = BankAccount("Alice", 1000)
account.deposit(500)
account.withdraw(200)
print(account.get_balance()) # 1300
# print(account.__balance) # ❌ AttributeError(私有属性)
print(account.balance) # 1300(property 访问)
account.balance = 2000 # setter只读属性
python
class Student:
def __init__(self, name, student_id):
self.name = name
self._student_id = student_id # 单下划线表示"请当作私有"
@property
def student_id(self):
"""只读属性——没有 setter"""
return self._student_id
s = Student("Alice", "S001")
print(s.student_id) # S001
# s.student_id = "S002" # ❌ AttributeError(没有 setter)7. 继承
单继承
python
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
pass
def move(self):
print(f"{self.name} is moving")
class Dog(Animal):
def speak(self):
print(f"{self.name} says 汪汪!")
class Cat(Animal):
def speak(self):
print(f"{self.name} says 喵喵!")
dog = Dog("旺财")
cat = Cat("咪咪")
dog.move() # 旺财 is moving(从 Animal 继承)
dog.speak() # 旺财 says 汪汪!(自己实现)
cat.speak() # 咪咪 says 喵喵!多继承
python
class Flyable:
def fly(self):
print("Flying...")
class Swimmable:
def swim(self):
print("Swimming...")
class Duck(Flyable, Swimmable):
def quack(self):
print("Quack!")
duck = Duck()
duck.fly() # Flying...
duck.swim() # Swimming...
duck.quack() # Quack!MRO(方法解析顺序)
python
class A:
def method(self):
print("A")
class B(A):
def method(self):
print("B")
class C(A):
def method(self):
print("C")
class D(B, C):
pass
d = D()
d.method() # B(按 MRO 顺序查找)
print(D.__mro__) # D → B → C → A → object
# Python 使用 C3 线性化算法确定 MRO子类复用父类方法
python
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 调用父类的 __init__
self.breed = breed
dog = Dog("旺财", "金毛")
print(dog.name) # 旺财
print(dog.breed) # 金毛8. 多态(Polymorphism)
Python 的"鸭子类型"——不关心对象的类型,只关心对象有没有该方法。
python
class Duck:
def quack(self):
print("鸭子呱呱叫")
class Person:
def quack(self):
print("人模仿鸭子叫")
def make_it_quack(thing):
# Python 多态:不检查类型,直接调用方法
thing.quack()
make_it_quack(Duck()) # 鸭子呱呱叫
make_it_quack(Person()) # 人模仿鸭子叫
# 只要实现了 __len__ 方法,就可以用 len()
class MyCollection:
def __len__(self):
return 100
print(len(MyCollection())) # 100练习题
练习 1:定义类
定义一个 Car 类,包含 start() 和 stop() 方法,分别打印"启动"和"停止"。
练习 2:封装
定义一个 Temperature 类,有一个私有属性 _celsius,通过 property 暴露,并添加华氏度转换方法。
练习 3:继承
定义一个 Shape 基类和 Circle / Rectangle 子类,计算各自的面积。
练习 4:多态
定义三个不同的"通知器"(Email/SMS/WeChat),都实现 send(message) 方法,然后用一个函数统一调用。
参考答案
练习 1:
python
class Car:
def __init__(self, brand):
self.brand = brand
def start(self):
print(f"{self.brand} 启动")
def stop(self):
print(f"{self.brand} 停止")练习 2:
python
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练习 3:
python
import math
class Shape:
def area(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return math.pi * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height练习 4:
python
class EmailNotifier:
def send(self, message):
print(f"[Email] {message}")
class SMSNotifier:
def send(self, message):
print(f"[SMS] {message}")
class WeChatNotifier:
def send(self, message):
print(f"[WeChat] {message}")
def notify(notifier, message):
notifier.send(message)
notify(EmailNotifier(), "Hello") # [Email] Hello
notify(SMSNotifier(), "Hello") # [SMS] Hello