03 - 面向对象基础¶
学习时间: 45-60分钟 重要性: ⭐⭐⭐⭐ 理解代码组织方式
🎯 学习目标¶
- 理解类和对象的概念
- 掌握属性和方法
- 理解继承和多态
- 知道何时使用OOP
🏗️ 类和对象¶
基本概念¶
Python
# 类是模板,对象是根据模板创建的实例
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 introduce(self):
return f"I'm {self.name}, {self.age} years old"
# 创建对象(实例化)
dog1 = Dog("Buddy", 3)
dog2 = Dog("Max", 5)
# 访问属性
print(dog1.name) # "Buddy"
print(dog2.age) # 5
# 调用方法
print(dog1.bark()) # "Buddy says Woof!"
print(dog2.introduce()) # "I'm Max, 5 years old"
# 类属性是共享的
print(dog1.species) # "Canis familiaris"
print(dog2.species) # "Canis familiaris"
类 vs 实例属性¶
Python
class Student:
school = "清华大学" # 类属性
def __init__(self, name):
self.name = name # 实例属性
# 类属性共享
s1 = Student("张三")
s2 = Student("李四")
print(s1.school) # "清华大学"
print(s2.school) # "清华大学"
# 修改类属性(影响所有实例)
Student.school = "北京大学"
print(s1.school) # "北京大学"
print(s2.school) # "北京大学"
# 实例属性独立
s1.name = "王五"
print(s1.name) # "王五"
print(s2.name) # "李四"(不受影响)
🔧 方法的类型¶
Python
class Calculator:
def __init__(self, value):
self.value = value
# 实例方法(最常用)
def add(self, x):
"""需要访问实例属性"""
return self.value + x
# @classmethod类方法:第一个参数cls代表类本身
@classmethod
def from_string(cls, str_value):
"""从字符串创建实例(工厂方法)"""
value = int(str_value)
return cls(value)
# @staticmethod静态方法:不需要self/cls,通过类名直接调用
@staticmethod
def is_valid(x):
"""不需要访问实例或类,只是相关的工具函数"""
return isinstance(x, (int, float)) # isinstance(obj, type)检查对象是否是指定类型(含子类)的实例
# 使用
calc = Calculator(10)
print(calc.add(5)) # 15
# 类方法:替代构造函数
calc2 = Calculator.from_string("20")
print(calc2.value) # 20
# 静态方法:不需要实例
print(Calculator.is_valid(10)) # True
print(Calculator.is_valid("abc")) # False
🔒 封装和访问控制¶
公共 vs 私有属性¶
Python
class BankAccount:
def __init__(self, balance):
self.balance = balance # 公共属性
self.__pin = "1234" # 私有属性(名称改写)
def deposit(self, amount):
if amount > 0:
self.balance += amount
def __verify_pin(self, pin):
"""私有方法"""
return self.__pin == pin
def withdraw(self, amount, pin):
if self.__verify_pin(pin):
if amount <= self.balance:
self.balance -= amount
return True
return False
account = BankAccount(1000)
account.deposit(500)
print(account.balance) # 1500
# 私有属性在外部无法直接访问
# print(account.__pin) # AttributeError!
# 但Python的"私有"是通过名称改写实现的
# 实际上可以通过 _BankAccount__pin 访问(不推荐)
print(account._BankAccount__pin) # "1234"
属性装饰器(推荐)¶
Python
class Temperature:
def __init__(self, celsius):
self._celsius = celsius # 约定:单下划线表示"内部使用"
@property # @property将方法变为属性访问,无需括号调用
def celsius(self):
"""获取摄氏度"""
return self._celsius
@celsius.setter
def celsius(self, value):
"""设置摄氏度(带验证)"""
if value < -273.15:
raise ValueError("Temperature below -273.15°C is not possible")
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
print(temp.fahrenheit) # 77.0
temp.celsius = 30
print(temp.fahrenheit) # 86.0
temp.fahrenheit = 100
print(temp.celsius) # 37.777...
# temp.celsius = -300 # ValueError!
🧬 继承¶
基本继承¶
Python
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
pass
def eat(self):
return f"{self.name} is eating"
# 子类继承父类
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"
# 使用
dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.speak()) # "Buddy says Woof!"
print(cat.speak()) # "Whiskers says Meow!"
print(dog.eat()) # "Buddy is eating"(继承自Animal)
调用父类方法¶
Python
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def introduce(self):
return f"I'm {self.name}, {self.age} years old"
class Student(Person):
def __init__(self, name, age, student_id):
# 调用父类构造函数
super().__init__(name, age)
self.student_id = student_id
def introduce(self):
# 调用父类方法,然后添加额外信息
base_intro = super().introduce()
return f"{base_intro}, ID: {self.student_id}"
# 使用
student = Student("张三", 20, "S1001")
print(student.introduce())
# "I'm 张三, 20 years old, ID: S1001"
方法重写与多态¶
Python
class Shape:
def area(self):
raise NotImplementedError("Subclass must implement area()")
def perimeter(self):
raise NotImplementedError("Subclass must implement perimeter()")
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
# 多态:统一接口,不同实现
shapes = [Rectangle(10, 5), Circle(7)]
for shape in shapes:
print(f"Area: {shape.area()}, Perimeter: {shape.perimeter()}")
🎯 何时使用OOP¶
适合使用类的场景¶
Python
# ✅ 适合:需要维护状态的复杂数据
class DataProcessor:
def __init__(self, filepath):
self.filepath = filepath
self.data = None
self.clean = False
def load(self):
self.data = pd.read_csv(self.filepath)
def clean_data(self):
# 复杂的清洗逻辑
self.clean = True
def save(self, output_path):
if self.clean:
self.data.to_csv(output_path)
# ✅ 适合:需要封装和验证
class BankAccount:
def __init__(self, initial_balance):
if initial_balance < 0:
raise ValueError("Initial balance cannot be negative")
self._balance = initial_balance
@property
def balance(self):
return self._balance
def deposit(self, amount):
if amount <= 0:
raise ValueError("Deposit must be positive")
self._balance += amount
不需要使用类的场景¶
Python
# ❌ 不适合:简单的数据处理
class MathOperations:
def add(self, a, b):
return a + b
def subtract(self, a, b):
return a - b
# ✅ 更好:直接使用函数
def add(a, b):
return a + b
def subtract(a, b):
return a - b
# ❌ 不适合:只是存储数据的容器
class PersonData:
def __init__(self, name, age, email):
self.name = name
self.age = age
self.email = email
# ✅ 更好:使用dataclass或字典
from dataclasses import dataclass
@dataclass # @dataclass自动生成__init__、__repr__等方法,简化类定义
class Person:
name: str
age: int
email: str
# 或者直接用字典
person = {"name": "张三", "age": 25, "email": "test@qq.com"}
💡 实用技巧¶
数据类(dataclass)- Python 3.7+¶
Python
from dataclasses import dataclass
@dataclass
class Student:
name: str
age: int
grades: list[int]
def average_grade(self):
return sum(self.grades) / len(self.grades) if self.grades else 0
# 自动生成__init__, __repr__, __eq__等方法
student = Student("张三", 20, [85, 90, 78])
print(student) # Student(name='张三', age=20, grades=[85, 90, 78])
print(student.average_grade()) # 84.333...
魔术方法(特殊方法)¶
Python
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
# __str__定义print()输出格式;__repr__定义调试/交互式显示格式
def __repr__(self):
return f"Vector({self.x}, {self.y})"
def __str__(self):
return f"({self.x}, {self.y})"
# 相等性比较
def __eq__(self, other):
if not isinstance(other, Vector):
return False
return self.x == other.x and self.y == other.y
# 运算符重载
def __add__(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 __len__(self):
return int((self.x**2 + self.y**2) ** 0.5)
# 使用
v1 = Vector(3, 4)
v2 = Vector(1, 2)
print(v1) # "(3, 4)"
print(repr(v1)) # "Vector(3, 4)"
print(v1 == v2) # False
print(v1 + v2) # Vector(4, 6)
print(v1 * 2) # Vector(6, 8)
print(len(v1)) # 5
上下文管理器¶
Python
class FileManager:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self): # __enter__/__exit__实现上下文管理器协议,支持with语句
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb): # exc_type=异常类型, exc_val=异常值, exc_tb=回溯对象;无异常时均为None
if self.file:
self.file.close()
# 可以处理异常
if exc_type:
print(f"An error occurred: {exc_val}")
return True # 返回True表示已处理异常不再向上传播;返回False/None则继续抛出
# 使用
with FileManager("test.txt", "w") as f:
f.write("Hello")
# 即使出错,文件也会自动关闭
📝 练习¶
练习1: 创建一个简单的类¶
Python
class Rectangle:
"""
实现一个矩形类,包含:
- 属性:width, height
- 方法:area(), perimeter(), is_square()
"""
# TODO: 实现这个类
pass
# 测试
rect = Rectangle(5, 5)
print(rect.area()) # 应该输出 25
print(rect.is_square()) # 应该输出 True
练习2: 继承与多态¶
Python
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
def calculate_bonus(self):
"""计算奖金,默认为工资的10%"""
return self.salary * 0.1
class Manager(Employee):
"""经理的奖金为工资的20%"""
# TODO: 重写calculate_bonus方法
pass
class Developer(Employee):
"""开发者的奖金为工资的15%"""
# TODO: 重写calculate_bonus方法
pass
# 测试
emp = Employee("张三", 5000)
mgr = Manager("李四", 8000)
dev = Developer("王五", 6000)
print(emp.calculate_bonus()) # 500
print(mgr.calculate_bonus()) # 1600
print(dev.calculate_bonus()) # 900
练习3: 使用属性装饰器¶
Python
class BankAccount:
"""
实现银行账户类:
- balance属性:只能通过deposit和withdraw修改
- deposit方法:存款
- withdraw方法:取款(不能透支)
- 使用@property装饰器
"""
# TODO: 实现这个类
pass
🎯 自我检查¶
完成这个主题后,你应该:
- 理解类和对象的概念
- 知道实例方法、类方法、静态方法的区别
- 能使用继承和方法重写
- 理解封装和属性装饰器
- 知道何时使用OOP,何时使用函数
- 不查资料完成上面的练习
📚 延伸阅读¶
下一步: 04 - 文件操作与异常处理