如何为使用attrs.field的Protocol添加类型提示?
如何为使用attrs.field的Protocol添加类型提示?
首先得指出你代码里的核心问题:不要给Protocol类加@frozen装饰器!Protocol是用来定义接口规范的抽象类型,不是具体的实现类,attrs的装饰器(比如@frozen)是给实际要实例化的具体类用的,给Protocol加装饰器只会导致类型混乱和运行时错误(比如你遇到的mutate报错)。
下面给你一套正确的实现方案,既能保留attrs的便捷特性(哈希、不可变等),又能完美结合Protocol模式,同时解决类型提示问题:
第一步:定义纯Protocol接口
先把Protocol类还原成纯接口,只声明属性和方法的签名,不用任何attrs装饰:
from typing import Protocol # 定义A接口:要求有int类型的a属性 class AProtocol(Protocol): a: int # 定义B接口:要求有str类型的b属性 class BProtocol(Protocol): b: str # 组合接口:同时满足A和B的要求,可额外添加方法声明 class CProtocol(AProtocol, BProtocol, Protocol): # 可选:声明该接口必须实现的方法 def validate(self) -> bool: ...
第二步:创建attrs实现类
接下来写具体的实现类,继承上面的Protocol,同时用attrs的装饰器赋予它哈希、不可变等特性:
from attrs import frozen, field @frozen class C(CProtocol): # 声明属性,严格匹配Protocol的类型要求 a: int b: str # 实现Protocol要求的方法 def validate(self) -> bool: return self.a > 0 and len(self.b) > 0
第三步:类型提示与使用
现在你可以在函数参数里用Protocol作为类型,类型检查工具(比如mypy、PyCharm)会自动验证传入的对象是否符合接口规范:
def process_c(obj: CProtocol) -> None: print(f"a: {obj.a}, b: {obj.b}") if obj.validate(): print("对象验证通过") # 实例化具体类并传入 c_instance = C(a=10, b="hello") process_c(c_instance) # 尝试直接修改frozen类的属性(会触发attrs的保护机制) # c_instance.a = 20 # 这行会报错,符合@frozen的预期 # 如果需要修改frozen对象,用attrs的mutate方法: from attrs import mutate with mutate(c_instance): c_instance.a = 20 print(c_instance.a) # 输出20,修改成功
关键说明
- Protocol与attrs的分工:Protocol负责定义"必须有什么",attrs负责实现具体类的属性管理、哈希、不可变等功能,二者各司其职,不会产生多重继承冲突。
- 之前报错的原因:你给Protocol加了@frozen,相当于把抽象接口变成了一个不可变的具体类,但Protocol本来就不是用来实例化的,这直接导致了后续操作(比如mutate)的逻辑混乱。
- 类型提示的正确性:只要你的实现类完全符合Protocol的要求,类型检查工具就会认可,不管你组合了多少个Protocol。
备注:内容来源于stack exchange,提问作者The unknown
相关产品推荐
相关产品推荐

