首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

Python 抽象基類 (ABC) 完整指南:定義介面、強制實作、虛擬子類與註冊機制

     編輯:更多程式設計知識

Python 的 `abc` (Abstract Base Classes) 模組提供正式的介面定義與實作強制機制,是大型專案架構設計、外掛系統、框架開發的核心工具。本文完整解析 ABC 定義、抽象方法/屬性、虛擬子類註冊與進階應用模式。

核心概念:ABCMeta 元類與裝飾器


from abc import ABCMeta, abstractmethod, abstractproperty, abstractclassmethod, abstractstaticmethod
# Python 3.3+ 建議直接繼承 abc.ABC
from abc import ABC

# 兩種定義方式 (Python 3.4+ 推薦方式 2)
class PluginBase(metaclass=ABCMeta):        # 方式 1: 明確指定元類
    pass

class PluginBase(ABC):                      # 方式 2: 繼承 ABC (內部使用 ABCMeta)
    pass

抽象方法與屬性:強制子類實作


from abc import ABC, abstractmethod, abstractproperty

class DataProcessor(ABC):
    """資料處理器介面"""
    
    @abstractmethod
    def process(self, data: bytes) -> dict:
        """核心處理邏輯,子類必須實作"""
        pass
    
    @abstractmethod
    def validate(self, data: bytes) -> bool:
        """資料驗證"""
        pass
    
    @abstractproperty
    def supported_formats(self) -> list[str]:
        """支援的格式列表"""
        pass
    
    @abstractclassmethod
    def from_config(cls, config: dict) -> 'DataProcessor':
        """工廠方法:從設定建立實例"""
        pass
    
    @abstractstaticmethod
    def get_default_config() -> dict:
        """預設設定"""
        pass

# 正確實作
class JsonProcessor(DataProcessor):
    @property
    def supported_formats(self) -> list[str]:
        return ['json', 'jsonl']
    
    def validate(self, data: bytes) -> bool:
        try:
            import json
            json.loads(data)
            return True
        except:
            return False
    
    def process(self, data: bytes) -> dict:
        import json
        return json.loads(data)
    
    @classmethod
    def from_config(cls, config: dict) -> 'JsonProcessor':
        return cls()
    
    @staticmethod
    def get_default_config() -> dict:
        return {"encoding": "utf-8"}

# 錯誤示範:未實作所有抽象成員
class IncompleteProcessor(DataProcessor):
    def process(self, data): pass
    # 缺少 validate, supported_formats 等

# 測試
try:
    p = IncompleteProcessor()  # TypeError: Can't instantiate abstract class...
except TypeError as e:
    print(f"預期錯誤: {e}")

# 正確使用
p = JsonProcessor()
print(p.process(b'{"key": "value"}'))

虛擬子類註冊:register() 機制

無需繼承,即可讓現有類別成為 ABC 的子類 (僅影響 `isinstance`/`issubclass` 檢查):


from abc import ABC, abstractmethod

class Serializable(ABC):
    @abstractmethod
    def serialize(self) -> bytes: pass
    
    @abstractmethod
    def deserialize(self, data: bytes): pass

# 現有類別,無法修改繼承關係
class LegacyData:
    def __init__(self, value):
        self.value = value
    
    def to_bytes(self):
        return str(self.value).encode()
    
    @classmethod
    def from_bytes(cls, data):
        return cls(data.decode())

# 註冊為虛擬子類 (不檢查抽象方法實作!)
Serializable.register(LegacyData)

# 驗證
obj = LegacyData(42)
print(isinstance(obj, Serializable))      # True
print(issubclass(LegacyData, Serializable)) # True

# 但無法直接當介面使用 (未實作抽象方法)
try:
    s: Serializable = obj
    s.serialize()  # AttributeError: 'LegacyData' object has no attribute 'serialize'
except AttributeError as e:
    print(f"需適配器模式: {e}")

# 適配器模式橋接
class LegacyAdapter(Serializable):
    def __init__(self, legacy: LegacyData):
        self._legacy = legacy
    
    def serialize(self) -> bytes:
        return self._legacy.to_bytes()
    
    def deserialize(self, data: bytes):
        self._legacy = LegacyData.from_bytes(data)

進階應用:外掛系統架構


from abc import ABC, abstractmethod
from typing import Dict, Type
import importlib
import pkgutil

class Plugin(ABC):
    """外掛基礎介面"""
    
    @property
    @abstractmethod
    def name(self) -> str: pass
    
    @property
    @abstractmethod
    def version(self) -> str: pass
    
    @abstractmethod
    def execute(self, context: dict) -> dict: pass
    
    @classmethod
    @abstractmethod
    def get_dependencies(cls) -> list[str]: pass

class PluginRegistry:
    """外掛註冊與發現中心"""
    
    def __init__(self):
        self._plugins: Dict[str, Type[Plugin]] = {}
        self._instances: Dict[str, Plugin] = {}
    
    def register(self, plugin_class: Type[Plugin]) -> None:
        if not issubclass(plugin_class, Plugin):
            raise TypeError("必須繼承 Plugin 基類")
        
        # 實例化以取得 name (或要求類別屬性)
        instance = plugin_class()
        if instance.name in self._plugins:
            raise ValueError(f"外掛名稱衝突: {instance.name}")
        
        self._plugins[instance.name] = plugin_class
        print(f"註冊外掛: {instance.name} v{instance.version}")
    
    def get_plugin(self, name: str) -> Plugin:
        if name not in self._instances:
            self._instances[name] = self._plugins[name]()
        return self._instances[name]
    
    def auto_discover(self, package_name: str) -> None:
        """自動發現套件中的外掛"""
        package = importlib.import_module(package_name)
        for _, module_name, _ in pkgutil.iter_modules(package.__path__):
            full_name = f"{package_name}.{module_name}"
            module = importlib.import_module(full_name)
            
            for attr_name in dir(module):
                attr = getattr(module, attr_name)
                if (isinstance(attr, type) and 
                    issubclass(attr, Plugin) and 
                    attr is not Plugin):
                    self.register(attr)

# 具體外掛實作
class ImageResizePlugin(Plugin):
    @property
    def name(self) -> str: return "image_resize"
    @property
    def version(self) -> str: return "1.0.0"
    
    def execute(self, context: dict) -> dict:
        # 實際處理邏輯
        return {"status": "resized", "width": context.get("width")}
    
    @classmethod
    def get_dependencies(cls) -> list[str]: return ["pillow"]

# 使用
registry = PluginRegistry()
registry.register(ImageResizePlugin)
plugin = registry.get_plugin("image_resize")
result = plugin.execute({"width": 800})

ABC 內建抽象基類:collections.abc

標準庫提供常用資料結構的 ABC,支援鴨子型別檢查:


from collections.abc import Iterable, Iterator, Sequence, Mapping, Callable, Container

# 判斷物件類型
def process_items(items):
    if isinstance(items, Mapping):
        print("字典類型")
    elif isinstance(items, Sequence) and not isinstance(items, str):
        print("序列類型 (list, tuple 等)")
    elif isinstance(items, Iterator):
        print("迭代器 (一次性)")
    elif isinstance(items, Iterable):
        print("可迭代物件")
    elif isinstance(items, Callable):
        print("可呼叫物件")
    else:
        print("其他")

# 自定義類別實作介面
class MyRange(Sequence):
    def __init__(self, start, end, step=1):
        self.start, self.end, self.step = start, end, step
    
    def __len__(self):
        return max(0, (self.end - self.start + self.step - 1) // self.step)
    
    def __getitem__(self, index):
        if index < 0: index += len(self)
        if index >= len(self): raise IndexError
        return self.start + index * self.step

r = MyRange(0, 10, 2)
print(list(r))           # [0, 2, 4, 6, 8]
print(isinstance(r, Sequence))  # True
print(2 in r)            # True (Container 介面)

Python 3.10+ 新語法:`@abstractmethod` 結合 `typing.Protocol`


from typing import Protocol
from abc import abstractmethod

# Protocol 結構化子類型 (靜態檢查,執行時無強制)
class Drawable(Protocol):
    @abstractmethod
    def draw(self) -> None: ...

class Circle:
    def draw(self) -> None:
        print("畫圓")

def render(obj: Drawable):
    obj.draw()

render(Circle())  # 靜態類型檢查通過,執行時正常
# render("not drawable")  # 型別檢查器會報錯

ABC vs Protocol vs Interface 比較

特性abc.ABCtyping.ProtocolJava Interface
執行時強制是 (實例化檢查)否 (僅靜態檢查)是
多重繼承支援支援 (隱式)支援
預設實作可提供不可 (3.14+ 可用 `@runtime_checkable`)可 (default method)
虛擬子類register() 支援不支援不支援
適用場景框架核心、外掛系統、執行時驗證鴨子型別、靜態分析、輕量介面強型別語言標準

最佳實務檢查清單

  • ✅ 使用 `from abc import ABC` 而非手動指定 `metaclass=ABCMeta`
  • ✅ 抽象方法加上型別提示增強可讀性與靜態分析
  • ✅ 屬性使用 `@property` + `@abstractmethod` (Python 3.3+)
  • ✅ 類別方法/靜態方法使用 `@abstractclassmethod`/`@abstractstaticmethod`
  • ✅ `register()` 僅用於無法修改的第三方類別,優先使用繼承
  • ✅ 結合 `collections.abc` 進行標準介面檢查
  • ✅ 文檔字串說明契約語義 (前置條件、後置條件、不變項)
  • ✅ 單元測試驗證:未實作抽象成員無法實例化、虛擬子類 isinstance 行為

善用 ABC 定義清晰契約,配合虛擬子類註冊與 Protocol 靜態檢查,能構建高內聚、低耦合、可擴充的 Python 系統架構。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved