python 抽象基类

1. 什么是抽象基类?

抽象基类是用于定义接口和规范的类,它不能被实例化,只能被继承。抽象基类可以包含抽象方法(子类必须实现的方法)和具体方法(子类可以直接使用的方法)。

2. 抽象基类的主要作用

  • 定义接口规范:强制子类实现特定的方法
  • 提供默认实现:在抽象基类中提供通用方法的实现
  • 类型检查 :可以使用isinstance()进行类型检查
  • 注册机制:允许将现有类注册为抽象基类的子类

3. 实现抽象基类的两种方式

方式一:使用abc模块

python 复制代码
from abc import ABC, abstractmethod

class Animal(ABC):
    """动物抽象基类"""
    
    def __init__(self, name):
        self.name = name
    
    @abstractmethod
    def make_sound(self):
        """发出声音的抽象方法"""
        pass
    
    @abstractmethod
    def move(self):
        """移动的抽象方法"""
        pass
    
    def eat(self, food):
        """具体方法 - 所有动物都可以吃"""
        print(f"{self.name} is eating {food}")

class Dog(Animal):
    """狗类 - 继承自Animal"""
    
    def make_sound(self):
        return f"{self.name} says: Woof!"
    
    def move(self):
        return f"{self.name} is running"

class Cat(Animal):
    """猫类 - 继承自Animal"""
    
    def make_sound(self):
        return f"{self.name} says: Meow!"
    
    def move(self):
        return f"{self.name} is jumping"

# 使用示例
dog = Dog("Buddy")
cat = Cat("Kitty")

print(dog.make_sound())  # Buddy says: Woof!
print(cat.move())        # Kitty is jumping
dog.eat("bones")         # Buddy is eating bones

# 尝试实例化抽象基类会报错
# animal = Animal("Generic")  # TypeError: Can't instantiate abstract class

方式二:使用元类ABCMeta

python 复制代码
from abc import ABCMeta, abstractmethod

class Shape(metaclass=ABCMeta):
    """图形抽象基类"""
    
    @abstractmethod
    def area(self):
        """计算面积的抽象方法"""
        pass
    
    @abstractmethod
    def perimeter(self):
        """计算周长的抽象方法"""
        pass
    
    def describe(self):
        """具体方法 - 描述图形"""
        return f"Area: {self.area()}, Perimeter: {self.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):
        return 3.14159 * self.radius ** 2
    
    def perimeter(self):
        return 2 * 3.14159 * self.radius

# 使用示例
rect = Rectangle(5, 3)
circle = Circle(4)

print(rect.describe())   # Area: 15, Perimeter: 16
print(circle.describe()) # Area: 50.26544, Perimeter: 25.13272

4. 抽象属性和静态方法

python 复制代码
from abc import ABC, abstractmethod

class DataProcessor(ABC):
    """数据处理抽象基类"""
    
    @property
    @abstractmethod
    def source(self):
        """数据源 - 抽象属性"""
        pass
    
    @staticmethod
    @abstractmethod
    def validate(data):
        """数据验证 - 抽象静态方法"""
        pass
    
    @classmethod
    @abstractmethod
    def create_default(cls):
        """创建默认实例 - 抽象类方法"""
        pass
    
    def process(self, data):
        """具体方法 - 处理数据"""
        if self.validate(data):
            return self._transform(data)
        raise ValueError("Invalid data")
    
    def _transform(self, data):
        """内部转换方法"""
        return f"Processed: {data}"

class CSVProcessor(DataProcessor):
    """CSV处理器"""
    
    @property
    def source(self):
        return "CSV file"
    
    @staticmethod
    def validate(data):
        return isinstance(data, str) and data.endswith('.csv')
    
    @classmethod
    def create_default(cls):
        return cls()

class JSONProcessor(DataProcessor):
    """JSON处理器"""
    
    @property
    def source(self):
        return "JSON file"
    
    @staticmethod
    def validate(data):
        return isinstance(data, dict)
    
    @classmethod
    def create_default(cls):
        return cls()

# 使用示例
csv_processor = CSVProcessor()
json_processor = JSONProcessor()

print(csv_processor.source)  # CSV file
print(csv_processor.process("data.csv"))  # Processed: data.csv

print(json_processor.validate({"name": "test"}))  # True

5. 抽象基类的注册机制

python 复制代码
from abc import ABC, abstractmethod

class Drawable(ABC):
    """可绘制对象的抽象基类"""
    
    @abstractmethod
    def draw(self):
        pass

# 一个已经存在的类,无法直接继承Drawable
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def draw(self):
        return f"Drawing point at ({self.x}, {self.y})"

# 注册Point为Drawable的子类
Drawable.register(Point)

# 现在Point可以被识别为Drawable的子类
p = Point(10, 20)
print(isinstance(p, Drawable))  # True
print(issubclass(Point, Drawable))  # True
print(p.draw())  # Drawing point at (10, 20)

6. Python内置的抽象基类

Python的collections.abc模块提供了许多常用的抽象基类:

python 复制代码
from collections.abc import Sequence, MutableMapping, Iterable

class MyList(Sequence):
    """自定义序列类"""
    
    def __init__(self, items):
        self._items = list(items)
    
    def __getitem__(self, index):
        return self._items[index]
    
    def __len__(self):
        return len(self._items)
    
    def __setitem__(self, index, value):
        self._items[index] = value
    
    def insert(self, index, value):
        self._items.insert(index, value)

# 使用内置抽象基类进行类型检查
def process_items(items):
    """处理可迭代对象"""
    if not isinstance(items, Iterable):
        raise TypeError("items must be iterable")
    
    if isinstance(items, MutableMapping):
        print("Processing mutable mapping")
    elif isinstance(items, Sequence):
        print("Processing sequence")
    
    for item in items:
        print(item)

# 测试
my_list = MyList([1, 2, 3])
process_items(my_list)
process_items({"a": 1, "b": 2})

7. 完整的实际应用示例

python 复制代码
from abc import ABC, abstractmethod
from typing import List, Dict, Any
import json
import csv
from pathlib import Path

class FileHandler(ABC):
    """文件处理器抽象基类"""
    
    def __init__(self, file_path: str):
        self.file_path = Path(file_path)
        self._validate_file()
    
    def _validate_file(self):
        """验证文件是否存在"""
        if not self.file_path.exists():
            raise FileNotFoundError(f"File not found: {self.file_path}")
    
    @abstractmethod
    def read(self) -> List[Dict[str, Any]]:
        """读取文件内容的抽象方法"""
        pass
    
    @abstractmethod
    def write(self, data: List[Dict[str, Any]]) -> None:
        """写入文件内容的抽象方法"""
        pass
    
    @abstractmethod
    def process(self) -> List[Dict[str, Any]]:
        """处理数据的抽象方法"""
        pass
    
    def get_file_info(self) -> Dict[str, Any]:
        """获取文件信息(具体方法)"""
        return {
            "name": self.file_path.name,
            "size": self.file_path.stat().st_size,
            "extension": self.file_path.suffix
        }

class CSVHandler(FileHandler):
    """CSV文件处理器"""
    
    def read(self) -> List[Dict[str, Any]]:
        with open(self.file_path, 'r', encoding='utf-8') as f:
            reader = csv.DictReader(f)
            return list(reader)
    
    def write(self, data: List[Dict[str, Any]]) -> None:
        if not data:
            return
        
        with open(self.file_path, 'w', encoding='utf-8', newline='') as f:
            writer = csv.DictWriter(f, fieldnames=data[0].keys())
            writer.writeheader()
            writer.writerows(data)
    
    def process(self) -> List[Dict[str, Any]]:
        data = self.read()
        # 示例处理:去除空值
        return [row for row in data if all(row.values())]

class JSONHandler(FileHandler):
    """JSON文件处理器"""
    
    def read(self) -> List[Dict[str, Any]]:
        with open(self.file_path, 'r', encoding='utf-8') as f:
            data = json.load(f)
            if isinstance(data, dict):
                return [data]
            return data
    
    def write(self, data: List[Dict[str, Any]]) -> None:
        with open(self.file_path, 'w', encoding='utf-8') as f:
            json.dump(data, f, indent=2, ensure_ascii=False)
    
    def process(self) -> List[Dict[str, Any]]:
        data = self.read()
        # 示例处理:添加处理标记
        for item in data:
            item['processed'] = True
            item['handler'] = self.__class__.__name__
        return data

# 工厂类(可选)
class FileHandlerFactory:
    """文件处理器工厂"""
    
    @staticmethod
    def create_handler(file_path: str) -> FileHandler:
        ext = Path(file_path).suffix.lower()
        
        handlers = {
            '.csv': CSVHandler,
            '.json': JSONHandler
        }
        
        handler_class = handlers.get(ext)
        if not handler_class:
            raise ValueError(f"Unsupported file extension: {ext}")
        
        return handler_class(file_path)

# 使用示例
if __name__ == "__main__":
    # 创建测试文件
    test_csv = Path("test.csv")
    with open(test_csv, 'w') as f:
        f.write("name,age,city\nAlice,25,Beijing\nBob,,Shanghai\nCharlie,30,Guangzhou")
    
    # 使用工厂创建处理器
    handler = FileHandlerFactory.create_handler("test.csv")
    
    # 使用抽象基类定义的方法
    print("File info:", handler.get_file_info())
    
    # 读取数据
    raw_data = handler.read()
    print("Raw data:", raw_data)
    
    # 处理数据
    processed_data = handler.process()
    print("Processed data:", processed_data)
    
    # 清理
    test_csv.unlink()

8. 抽象基类的最佳实践

  1. 明确接口设计:抽象基类应该定义清晰、一致的接口
  2. 适度使用:不要过度使用抽象基类,只在需要定义接口规范时使用
  3. 文档完善:为抽象方法提供详细的文档字符串
  4. 提供默认实现:适当时可以提供默认的方法实现
  5. 使用类型提示:结合类型提示提高代码可读性

总结

抽象基类是Python中实现面向接口编程的重要工具,它可以帮助我们:

  • 定义清晰的接口规范
  • 实现代码复用
  • 提高代码的可维护性和可扩展性
  • 支持鸭子类型的同时提供类型检查能力

通过合理使用抽象基类,可以构建更加健壮和灵活的Python应用程序。

相关推荐
用户8356290780517 小时前
Python 实现 PPT 转 HTML
后端·python
zone773913 小时前
004:RAG 入门-LangChain读取PDF
后端·python·面试
zone773913 小时前
005:RAG 入门-LangChain读取表格数据
后端·python·agent
树獭非懒1 天前
AI大模型小白手册|Embedding 与向量数据库
后端·python·llm
唐叔在学习1 天前
就算没有服务器,我照样能够同步数据
后端·python·程序员
曲幽1 天前
FastAPI流式输出实战与避坑指南:让AI像人一样“边想边说”
python·ai·fastapi·web·stream·chat·async·generator·ollama
Flittly1 天前
【从零手写 AI Agent:learn-claude-code 项目实战笔记】(1)The Agent Loop (智能体循环)
python·agent
vivo互联网技术1 天前
ICLR2026 | 视频虚化新突破!Any-to-Bokeh 一键生成电影感连贯效果
人工智能·python·深度学习