Python面向对象进阶 🚀
1. 继承与多态 🔄
1.1 继承基础
python">class Animal:def __init__(self, name, age):self.name = nameself.age = agedef speak(self):passdef describe(self):return f"{self.name} is {self.age} years old"class Dog(Animal):def __init__(self, name, age, breed):super().__init__(name, age) # 调用父类的初始化方法self.breed = breeddef speak(self):return f"{self.name} says Woof!"def describe(self):# 扩展父类的方法return f"{super().describe()} and is a {self.breed}"class Cat(Animal):def __init__(self, name, age, indoor=True):super().__init__(name, age)self.indoor = indoordef speak(self):return f"{self.name} says Meow!"def describe(self):habitat = "indoor" if self.indoor else "outdoor"return f"{super().describe()} and is an {habitat} cat"# 使用示例
dog = Dog("Buddy", 3, "Golden Retriever")
cat = Cat("Whiskers", 2, True)print(dog.describe()) # 输出: Buddy is 3 years old and is a Golden Retriever
print(cat.describe()) # 输出: Whiskers is 2 years old and is an indoor cat
继承的关键概念:
super()
的使用:调用父类方法- 方法重写与扩展
- 构造函数的继承与扩展
- 属性的继承与新增
1.2 多态
让我们通过一个更实际的例子来展示多态的威力:
python">class Document:def __init__(self, content):self.content = contentdef format(self):passclass PlainText(Document):def format(self):return self.contentclass HTMLDocument(Document):def format(self):return f"<html><body>{self.content}</body></html>"class MarkdownDocument(Document):def format(self):return f"# {self.content}"def process_documents(documents):"""多态函数 - 统一处理不同类型的文档"""for doc in documents:print(f"处理 {doc.__class__.__name__}:")print(doc.format())print("---")# 实际应用
documents = [PlainText("Hello World"),HTMLDocument("Welcome"),MarkdownDocument("Title")
]process_documents(documents)
1.3 多重继承
让我们通过一个更实用的场景来展示多重继承:
python">class Persistable:"""提供持久化功能的混入类"""def save(self, filename):with open(filename, 'w') as f:f.write(str(self.__dict__))def load(self, filename):with open(filename, 'r') as f:data = eval(f.read())self.__dict__.update(data)class Loggable:"""提供日志功能的混入类"""def log(self, message):print(f"[{self.__class__.__name__}] {message}")class Configuration(Persistable, Loggable):"""应用配置类"""def __init__(self, host="localhost", port=8000):self.host = hostself.port = portdef update_config(self, **kwargs):self.log("Updating configuration...")self.__dict__.update(kwargs)self.log("Configuration updated")# 使用示例
config = Configuration()
config.update_config(host="127.0.0.1", port=5000)
config.save("config.txt") # 保存配置
config.load("config.txt") # 加载配置
2. 封装与访问控制 🔒
让我们通过一个更完整的例子来展示Python的封装机制:
python">class Employee:def __init__(self, name, salary):self._name = name # 受保护的属性self.__salary = salary # 私有属性self.__bonus = 0 # 私有属性@propertydef name(self):"""只读属性"""return self._name@propertydef total_salary(self):"""计算总薪资(基本工资 + 奖金)"""return self.__salary + self.__bonus@propertydef bonus(self):"""奖金查询"""return self.__bonus@bonus.setterdef bonus(self, value):"""设置奖金,带验证"""if not isinstance(value, (int, float)):raise TypeError("奖金必须是数字")if value < 0:raise ValueError("奖金不能为负数")self.__bonus = valuedef _calculate_tax(self):"""受保护的方法 - 计算税收"""return self.total_salary * 0.2def get_salary_info(self):"""公开方法 - 获取薪资信息"""return {'name': self._name,'total_salary': self.total_salary,'tax': self._calculate_tax(),'net_salary': self.total_salary - self._calculate_tax()}# 使用示例
emp = Employee("John Doe", 5000)# 属性访问
print(emp.name) # 使用@property
emp.bonus = 1000 # 使用@bonus.setter
print(emp.total_salary) # 使用@property计算总薪资# 获取完整信息
salary_info = emp.get_salary_info()
for key, value in salary_info.items():print(f"{key}: {value}")# 以下操作会引发错误
# emp.name = "Jane" # AttributeError: 不能修改只读属性
# emp.__salary = 6000 # AttributeError: 私有属性不能直接访问
# emp.bonus = -100 # ValueError: 奖金不能为负数
补充说明:
- 使用
@property
创建只读属性 - 使用
@property
和@x.setter
创建可读写属性 - 使用单下划线表示受保护成员
- 使用双下划线表示私有成员
- 通过公开方法提供对私有数据的安全访问
3. 类方法与静态方法 🛠️
3.1 类方法(@classmethod)
让我们通过一个更实用的工厂模式示例来展示类方法的应用:
python">from datetime import datetime, dateclass Order:"""订单类"""order_count = 0 # 类变量,用于追踪订单数量def __init__(self, customer_id: str, items: list, order_date: date):Order.order_count += 1self.order_id = f"ORD{Order.order_count:04d}"self.customer_id = customer_idself.items = itemsself.order_date = order_dateself.status = "pending"@classmethoddef create_from_dict(cls, order_data: dict):"""从字典创建订单对象"""try:customer_id = order_data['customer_id']items = order_data['items']# 解析日期字符串date_str = order_data.get('date', datetime.now().strftime('%Y-%m-%d'))order_date = datetime.strptime(date_str, '%Y-%m-%d').date()return cls(customer_id, items, order_date)except KeyError as e:raise ValueError(f"缺少必要的订单信息: {e}")@classmethoddef create_rush_order(cls, customer_id: str, items: list):"""创建加急订单"""order = cls(customer_id, items, date.today())order.status = "rush"return order@classmethoddef get_order_count(cls) -> int:"""获取订单总数"""return cls.order_countdef __str__(self):return f"Order {self.order_id}: {len(self.items)} items for {self.customer_id}"# 使用示例
# 1. 常规创建
order1 = Order("CUST001", ["item1", "item2"], date.today())# 2. 从字典创建
order_data = {"customer_id": "CUST002","items": ["item3", "item4"],"date": "2024-03-15"
}
order2 = Order.create_from_dict(order_data)# 3. 创建加急订单
rush_order = Order.create_rush_order("CUST003", ["urgent_item"])print(f"Total orders: {Order.get_order_count()}") # 输出订单总数
3.2 静态方法(@staticmethod)
通过一个更复杂的示例来展示静态方法的实际应用:
python">class DataValidator:"""数据验证工具类"""@staticmethoddef validate_email(email: str) -> bool:"""验证邮箱格式"""import repattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'return bool(re.match(pattern, email))@staticmethoddef validate_phone(phone: str) -> bool:"""验证电话号码格式"""import re# 支持多种格式:+86-123-4567-8901, 12345678901, 123-4567-8901patterns = [r'^\+\d{1,3}-\d{3}-\d{4}-\d{4}$',r'^\d{11}$',r'^\d{3}-\d{4}-\d{4}$']return any(bool(re.match(pattern, phone)) for pattern in patterns)@staticmethoddef validate_date(date_str: str) -> bool:"""验证日期格式和有效性"""try:datetime.strptime(date_str, '%Y-%m-%d')return Trueexcept ValueError:return False@staticmethoddef sanitize_string(text: str) -> str:"""清理并验证字符串"""# 移除首尾空白,替换多个空格为单个空格text = ' '.join(text.split())# 移除特殊字符text = ''.join(char for char in text if char.isprintable())return textclass UserProfile:"""用户档案类 - 展示数据验证的应用"""def __init__(self, email: str, phone: str, birth_date: str):if not DataValidator.validate_email(email):raise ValueError("无效的邮箱地址")if not DataValidator.validate_phone(phone):raise ValueError("无效的电话号码")if not DataValidator.validate_date(birth_date):raise ValueError("无效的出生日期")self.email = emailself.phone = phoneself.birth_date = birth_date# 使用示例
try:# 创建用户档案user = UserProfile(email="john.doe@example.com",phone="123-4567-8901",birth_date="1990-01-01")print("用户档案创建成功")# 验证和清理数据text = " Hello World! \n\t@#$% "cleaned = DataValidator.sanitize_string(text)print(f"清理后的文本: '{cleaned}'")except ValueError as e:print(f"错误: {e}")
4. 抽象类与接口 🎯
让我们通过一个文件处理系统的例子来展示抽象类和接口的实际应用:
python">from abc import ABC, abstractmethod
from typing import Any, List, Dict
import json
import csv
import xml.etree.ElementTree as ETclass DataProcessor(ABC):"""数据处理器抽象基类"""@abstractmethoddef read(self, source: str) -> Any:"""读取数据"""pass@abstractmethoddef write(self, data: Any, destination: str) -> bool:"""写入数据"""pass@abstractmethoddef validate(self, data: Any) -> bool:"""验证数据"""pass@property@abstractmethoddef supported_extensions(self) -> List[str]:"""支持的文件扩展名"""passclass JSONProcessor(DataProcessor):"""JSON数据处理器"""@propertydef supported_extensions(self) -> List[str]:return ['.json']def validate(self, data: Any) -> bool:if not isinstance(data, (dict, list)):return Falsetry:json.dumps(data) # 测试是否可序列化return Trueexcept (TypeError, ValueError):return Falsedef read(self, source: str) -> Any:try:with open(source, 'r', encoding='utf-8') as f:return json.load(f)except json.JSONDecodeError as e:raise ValueError(f"JSON解析错误: {e}")def write(self, data: Any, destination: str) -> bool:if not self.validate(data):raise ValueError("无效的JSON数据")try:with open(destination, 'w', encoding='utf-8') as f:json.dump(data, f, indent=2, ensure_ascii=False)return Trueexcept Exception as e:print(f"写入错误: {e}")return Falseclass CSVProcessor(DataProcessor):"""CSV数据处理器"""@propertydef supported_extensions(self) -> List[str]:return ['.csv']def validate(self, data: Any) -> bool:return isinstance(data, list) and all(isinstance(row, dict) for row in data)def read(self, source: str) -> List[Dict]:try:with open(source, 'r', encoding='utf-8') as f:return list(csv.DictReader(f))except Exception as e:raise ValueError(f"CSV读取错误: {e}")def write(self, data: List[Dict], destination: str) -> bool:if not self.validate(data):raise ValueError("无效的CSV数据格式")try:with open(destination, 'w', newline='', encoding='utf-8') as f:if not data:return Truewriter = csv.DictWriter(f, fieldnames=data[0].keys())writer.writeheader()writer.writerows(data)return Trueexcept Exception as e:print(f"写入错误: {e}")return Falseclass DataProcessorFactory:"""数据处理器工厂"""_processors: Dict[str, DataProcessor] = {'.json': JSONProcessor(),'.csv': CSVProcessor()}@classmethoddef get_processor(cls, file_path: str) -> DataProcessor:"""根据文件扩展名获取相应的处理器"""import osext = os.path.splitext(file_path)[1].lower()processor = cls._processors.get(ext)if not processor:raise ValueError(f"不支持的文件类型: {ext}")return processor# 使用示例
def convert_file(source: str, destination: str):"""文件转换功能"""try:# 获取源文件和目标文件的处理器source_processor = DataProcessorFactory.get_processor(source)dest_processor = DataProcessorFactory.get_processor(destination)# 读取源文件data = source_processor.read(source)# 写入目标文件if dest_processor.write(data, destination):print(f"文件转换成功: {source} -> {destination}")else:print("文件转换失败")except Exception as e:print(f"转换错误: {e}")# 测试文件转换
if __name__ == "__main__":# JSON到CSV的转换convert_file("data.json", "output.csv")# CSV到JSON的转换convert_file("data.csv", "output.json")
5. 实战案例:图形计算器 📐
让我们创建一个图形计算器,它能够处理不同类型的图形,计算它们的面积和周长:
python">from abc import ABC, abstractmethod
from typing import List
import mathclass Shape(ABC):"""抽象基类:形状"""@abstractmethoddef area(self) -> float:"""计算面积"""pass@abstractmethoddef perimeter(self) -> float:"""计算周长"""pass@abstractmethoddef description(self) -> str:"""返回形状描述"""passclass Rectangle(Shape):"""矩形类"""def __init__(self, width: float, height: float):if width <= 0 or height <= 0:raise ValueError("矩形的宽和高必须为正数")self._width = widthself._height = heightdef area(self) -> float:return self._width * self._heightdef perimeter(self) -> float:return 2 * (self._width + self._height)def description(self) -> str:return f"矩形(宽={self._width}, 高={self._height})"class Circle(Shape):"""圆形类"""def __init__(self, radius: float):if radius <= 0:raise ValueError("圆的半径必须为正数")self._radius = radiusdef area(self) -> float:return math.pi * self._radius ** 2def perimeter(self) -> float:return 2 * math.pi * self._radiusdef description(self) -> str:return f"圆形(半径={self._radius})"class Triangle(Shape):"""三角形类"""def __init__(self, a: float, b: float, c: float):if not self._is_valid_triangle(a, b, c):raise ValueError("无效的三角形边长")self._sides = (a, b, c)@staticmethoddef _is_valid_triangle(a: float, b: float, c: float) -> bool:"""检查三条边是否能构成三角形"""return (a > 0 and b > 0 and c > 0 anda + b > c and b + c > a and a + c > b)def area(self) -> float:"""使用海伦公式计算面积"""a, b, c = self._sidess = (a + b + c) / 2 # 半周长return math.sqrt(s * (s - a) * (s - b) * (s - c))def perimeter(self) -> float:return sum(self._sides)def description(self) -> str:return f"三角形(边长={', '.join(map(str, self._sides))})"class GeometryCalculator:"""几何计算器类"""def __init__(self):self._shapes: List[Shape] = []def add_shape(self, shape: Shape):"""添加形状"""self._shapes.append(shape)print(f"已添加: {shape.description()}")def calculate_total_area(self) -> float:"""计算所有形状的总面积"""return sum(shape.area() for shape in self._shapes)def calculate_total_perimeter(self) -> float:"""计算所有形状的总周长"""return sum(shape.perimeter() for shape in self._shapes)def show_all_shapes(self):"""显示所有形状的信息"""print("\n形状列表:")for i, shape in enumerate(self._shapes, 1):print(f"{i}. {shape.description()}")print(f" 面积: {shape.area():.2f}")print(f" 周长: {shape.perimeter():.2f}")def main():# 创建计算器实例calculator = GeometryCalculator()# 添加一些形状try:calculator.add_shape(Rectangle(5, 3))calculator.add_shape(Circle(4))calculator.add_shape(Triangle(3, 4, 5))# 显示所有形状信息calculator.show_all_shapes()# 显示总计print(f"\n总面积: {calculator.calculate_total_area():.2f}")print(f"总周长: {calculator.calculate_total_perimeter():.2f}")except ValueError as e:print(f"错误: {e}")if __name__ == "__main__":main()
实战案例特点:
- 抽象基类:使用
Shape
作为抽象基类,定义了必须实现的方法 - 继承与多态:各种具体图形类继承自
Shape
类 - 封装:使用私有属性保护数据
- 类型提示:使用
typing
模块增加代码可读性 - 错误处理:包含适当的验证和异常处理
- 静态方法:用于辅助功能(如三角形的有效性检查)
扩展建议:
- 添加更多图形类型(如多边形、椭圆等)
- 实现图形的缩放和旋转
- 添加图形的绘制功能
- 实现文件保存和加载功能
- 添加图形组合功能
- 实现简单的GUI界面
- 添加单位转换功能
这个实战案例展示了面向对象编程的进阶概念,包括继承、多态、封装和抽象类的实际应用。它提供了一个可扩展的框架,可以根据需要添加更多功能。
如果你觉得这篇文章有帮助,欢迎点赞转发,也期待在评论区看到你的想法和建议!👇
咱们下一期见!