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

如何为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 08:43:17