如何为兼具属性与同名键的对象定义Python类型注解?
解决方案:让对象同时支持属性与键访问的类型注解
通用方案(适用于任意类似对象)
你之前尝试混合继承TypedDict和Protocol的方法不可行,因为两者元类冲突,运行时会报错。正确的做法是用Protocol同时定义属性和索引器,让类型检查器识别两种访问方式:
from typing import Protocol, cast, Literal class MyHybridProtocol(Protocol): # 定义属性访问的类型 foo: str bar: int # 定义键访问的类型,限定允许的键和对应类型 def __getitem__(self, key: Literal["foo"]) -> str: ... def __getitem__(self, key: Literal["bar"]) -> int: ... def __setitem__(self, key: Literal["foo"], value: str) -> None: ... def __setitem__(self, key: Literal["bar"], value: int) -> None: ... # 给外部传入的obj做类型转换 myobj = ... myobj = cast(MyHybridProtocol, myobj)
这样配置后,输入obj.或obj["时,Pylance/Pyright会自动弹出foo和bar的补全提示,且类型检查会匹配对应访问方式的类型。
如果需要复用TypedDict的定义,可以结合Literal简化索引器代码:
from typing import Protocol, TypedDict, cast, Literal class MyDict(TypedDict): foo: str bar: int # 提取TypedDict的键为Literal类型 AllowedKeys = Literal["foo", "bar"] class MyHybridProtocol(Protocol): foo: str bar: int def __getitem__(self, key: AllowedKeys) -> MyDict[AllowedKeys]: ... def __setitem__(self, key: AllowedKeys, value: MyDict[AllowedKeys]) -> None: ... myobj = ... myobj = cast(MyHybridProtocol, myobj)
Streamlit会话状态专属方案
针对st.session_state的场景,有更简洁的官方推荐方案:
方法1:直接用TypedDict注解(推荐)
Streamlit的会话状态实现了属性与键的双向映射,Pylance/Pyright可以直接识别TypedDict注解,并同时支持两种访问方式的智能提示:
from typing import TypedDict import streamlit as st class SessionState(TypedDict): foo: str bar: int # 给st.session_state绑定类型 st.session_state: SessionState # 此时两种访问方式都会得到正确提示 st.session_state.foo = "hello" st.session_state["bar"] = 42
方法2:专用Protocol约束
如果需要更严格的行为定义,可以针对会话状态编写专属Protocol:
from typing import Protocol, Literal, cast import streamlit as st class SessionStateProtocol(Protocol): foo: str bar: int def __getitem__(self, key: Literal["foo"]) -> str: ... def __getitem__(self, key: Literal["bar"]) -> int: ... def __setitem__(self, key: Literal["foo"], value: str) -> None: ... def __setitem__(self, key: Literal["bar"], value: int) -> None: ... st.session_state = cast(SessionStateProtocol, st.session_state)
内容的提问来源于stack exchange,提问作者Diogo
相关产品推荐
相关产品推荐

