Python 多态、泛型、协议 简单结合应用

from typing import Generic, TypeVar, Dict, Type, Protocol, runtime_checkable

# 动物基类
class Animal:
    """动物基类"""
    def make_sound(self) -> str:
        raise NotImplementedError("子类必须实现这个方法")

# 具体动物类
class Dog(Animal):
    def make_sound(self) -> str:
        return "汪汪汪"

class Cat(Animal):
    def make_sound(self) -> str:
        return "喵喵喵"

class Bird(Animal):
    def make_sound(self) -> str:
        return "叽叽叽"

# 定义动物类型标识的协议
@runtime_checkable
class AnimalType(Protocol):
    """动物类型标识的协议"""
    @property
    def animal_cls(self) -> Type[Animal]:
        ...

# 具体的动物类型标识
class DogType:
    @property
    def animal_cls(self) -> Type[Dog]:
        return Dog

class CatType:
    @property
    def animal_cls(self) -> Type[Cat]:
        return Cat

class BirdType:
    @property
    def animal_cls(self) -> Type[Bird]:
        return Bird

# 定义泛型类型变量
T = TypeVar('T', bound=AnimalType)

class AnimalSound(Generic[T]):
    """处理动物叫声的泛型类"""
    
    def __init__(self) -> None:
        # 维护动物类型标识与实例的映射(使用实例而非类型作为键)
        self.animal_instances: Dict[AnimalType, Animal] = {
            DogType(): Dog(),
            CatType(): Cat(),
            BirdType(): Bird()
        }
    
    def get_sound(self, animal_type: T) -> str:
        """根据动物类型获取对应的叫声,使用isinstance检查"""
        # 首先检查传入的类型是否符合AnimalType协议
        if not isinstance(animal_type, AnimalType):
            return f"传入的类型 {type(animal_type).__name__} 不符合AnimalType协议"
        
        # 遍历映射,找到匹配的动物类型
        for registered_type, animal_instance in self.animal_instances.items():
            # 检查类型是否匹配(基于结构的匹配)
            if isinstance(animal_type, type(registered_type)):
                return animal_instance.make_sound()
        
        return f"未知动物类型: {type(animal_type).__name__}"

# 使用示例
if __name__ == "__main__":
    sound_handler = AnimalSound()
    
    # 测试已知动物类型
    print(sound_handler.get_sound(DogType()))   # 输出:汪汪汪
    print(sound_handler.get_sound(CatType()))   # 输出:喵喵喵
    print(sound_handler.get_sound(BirdType()))  # 输出:叽叽叽
    
    # 测试不符合协议的类型
    class InvalidType:
        pass  # 缺少animal_cls属性,不符合协议
    
    print(sound_handler.get_sound(InvalidType()))  # 输出:传入的类型 InvalidType 不符合AnimalType协议
    
    # 测试新动物类型
    class Fish(Animal):
        def make_sound(self) -> str:
            return "咕噜噜"
    
    class FishType:
        @property
        def animal_cls(self) -> Type[Fish]:
            return Fish
    
    # 添加新动物到处理器
    sound_handler.animal_instances[FishType()] = Fish()
    print(sound_handler.get_sound(FishType()))  # 输出:咕噜噜

这段代码定义了一个名为AnimalType的协议(Protocol),用于规范动物类型标识类的结构。我们来逐部分解析:

@runtime_checkable 装饰器

  • 这是typing模块中的一个装饰器,用于使协议支持运行时类型检查
  • 没有这个装饰器,isinstance()等检查无法用于协议类型

class AnimalType(Protocol)

  • 定义了一个继承自Protocol的类,这是 Python 3.8 + 引入的功能
  • 协议(Protocol)是一种特殊的类,用于定义 "接口" 规范,不强制继承,但要求实现指定方法 / 属性

@property 装饰器

  • 定义了一个属性接口,要求实现类必须提供一个animal_cls属性

def animal_cls(self) -> Type[Animal]

  • 声明animal_cls属性的返回类型必须是Animal类的类型(即类本身,而非实例)
  • Type[Animal]表示 "Animal 类或其任何子类的类型"

...

  • 这是 Python 中的省略号,表示方法体的实现由具体子类提供

这个协议的作用是:规定所有 "动物类型标识类" 都必须提供一个animal_cls属性,该属性返回对应的动物类(如DogCat等)。

在 Python 中,@runtime_checkable 装饰器与 isinstance 结合使用时的运行机制,和普通 isinstance 检查有本质区别,核心在于从 “名义子类型检查” 变成了 “结构子类型检查”

1. 普通 isinstance 的运行机制(名义子类型检查)

普通的 isinstance(obj, cls) 检查的是 **“继承关系”**,属于 “名义子类型”(nominal subtyping):

  • 只有当 obj 的类是 cls 的直接子类、间接子类,或者 obj 是 cls 的实例时,才返回 True

2. @runtime_checkable 修饰的协议与 isinstance(结构子类型检查)

当用 @runtime_checkable 修饰 Protocol 子类(协议)时,isinstance(obj, ProtocolClass) 的运行机制完全不同,它基于 **“结构匹配”**,属于 “结构子类型”(structural subtyping):

不关心 obj 的类是否显式继承了协议,只检查 obj 是否实际拥有协议中定义的所有成员(方法、属性等)。

它要求符合协议的对象必须有一个 animal_cls 属性(且类型匹配)。

当检查 isinstance(DogType(), AnimalType) 时,Python 会在运行时做两件事:

检查 DogType 实例是否有 animal_cls 属性;

检查该属性的类型是否符合协议中声明的 Type[Animal]
只要这两点满足,无论 DogType 是否显式继承 AnimalType,都会返回 True

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值