You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为使用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,修改成功

关键说明

  1. Protocol与attrs的分工:Protocol负责定义"必须有什么",attrs负责实现具体类的属性管理、哈希、不可变等功能,二者各司其职,不会产生多重继承冲突。
  2. 之前报错的原因:你给Protocol加了@frozen,相当于把抽象接口变成了一个不可变的具体类,但Protocol本来就不是用来实例化的,这直接导致了后续操作(比如mutate)的逻辑混乱。
  3. 类型提示的正确性:只要你的实现类完全符合Protocol的要求,类型检查工具就会认可,不管你组合了多少个Protocol。

备注:内容来源于stack exchange,提问作者The unknown

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 08:49:32