如何为Python Protocol定义仅写(write-only)属性?
如何为typing.Protocol定义仅写属性
先回顾下Python官方文档关于Protocol属性的规则:
默认情况下,直接定义的协议变量被视为可读可写。要定义只读协议变量,可使用抽象属性。
只读属性的协变特性示例如下:
from typing import Protocol class A(): pass class B(A): pass # 定义协议 class HasA(Protocol): a: A # 可读可写 class HasA_RO(Protocol): @property def a(self) -> A: ... # 只读属性 # 实现类 class Impl(): a: B
此时Impl能匹配HasA_RO但无法匹配HasA,验证代码:
impl1: HasA = Impl() # 类型错误:Impl的a是B类型,无法满足HasA的可读可写(写入A类型会不兼容) impl2: HasA_RO = Impl() # 正常:只读属性支持协变,B是A的子类,读取时可以向上兼容 a = A() impl1.a = a # 对HasA合法,但Impl的a实际是B类型,赋值A会有类型问题 impl2.a = a # 正确被禁止,因为是只读属性
仅写属性的实现方案
Python的typing模块目前没有原生的WriteOnly类型,但可以通过抽象setter方法来模拟仅写协议属性,以此实现逆变性:
from typing import Protocol class A(): pass class B(A): pass class HasB_WO(Protocol): @property def b(self) -> None: ... # 定义空的getter,明确禁止读取操作 @b.setter def b(self, value: B) -> None: ... # 仅允许写入B类型的值 class Impl2(): b: A # 类型检查通过:B是A的子类,将B类型的值写入A类型的字段完全合法,符合逆变性要求 i2: HasB_WO = Impl2() # 合法
如果觉得定义空getter麻烦,也可以直接定义__setattr__方法,但这种方式不够直观,部分类型检查器的识别精度可能稍差:
class HasB_WO(Protocol): def __setattr__(self, name: str, value: B) -> None: ...
核心逻辑是:当协议要求可以写入B类型,那么实现类的属性类型是A(B的父类)是兼容的——因为所有B的实例都可以安全赋值给A类型的变量,这正是仅写属性所需的逆变性特性。
内容的提问来源于stack exchange,提问作者tbrugere
相关产品推荐
相关产品推荐

