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属性,该属性返回对应的动物类(如Dog、Cat等)。
在 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。
234

被折叠的 条评论
为什么被折叠?



