为何能重载typing.Dict的__getattr__,却无法重载typing_extensions.TypedDict?
问题原因与解决方案
为什么typing_extensions.TypedDict子类的__getattr__会被忽略?
TypedDict本质上是仅用于类型注解的虚拟类,Python解释器在运行时会将其子类实例化为普通dict对象,本不应保留自定义方法。但typing.TypedDict和typing_extensions.TypedDict的运行时实现逻辑存在差异:
- Python 3.10的标准库
typing.TypedDict元类处理逻辑,意外保留了子类中的自定义方法; typing_extensions.TypedDict的实现更严格遵循TypedDict的设计初衷,完全剥离子类自定义方法,直接返回原生dict实例,导致重载的__getattr__无法执行。
解决办法
方案1:类型注解与运行时类分离
单独定义用于类型检查的TypedDict,再用继承dict的类实现运行时属性访问逻辑:
from typing_extensions import TypedDict, NotRequired # 仅用于类型注解的TypedDict class MyData(TypedDict): a: int b: NotRequired[str] # 实现属性访问的自定义dict子类 class AttrDict(dict): def __getattr__(self, key): return self[key] # 结合使用 f: MyData = AttrDict(a=1) print(f['a']) # 输出1 print(f.a) # 输出1
这种方式既符合PEP 655要求使用typing_extensions的NotRequired,又能实现预期的运行时行为。
方案2:自定义元类修正实例化逻辑
通过元类覆盖typing_extensions.TypedDict的默认实例化行为,强制保留自定义方法:
from typing_extensions import TypedDict class AttrTypedDictMeta(type): def __call__(cls, **kwargs): instance = super().__call__(**kwargs) instance.__class__ = cls return instance class WorkingDict(TypedDict, metaclass=AttrTypedDictMeta): a: int def __getattr__(self, key): return self[key] f = WorkingDict(a=1) print(f['a']) # 输出1 print(f.a) # 输出1
注意:此方法属于hack手段,可能随Python或typing_extensions版本更新失效,仅推荐临时场景使用。
方案3:升级Python版本(长期方案)
Python 3.11及以上版本的标准库typing已内置NotRequired,可直接从typing导入TypedDict和NotRequired,沿用typing.TypedDict子类保留方法的行为。
关于PEP标准的说明
PEP 655允许在Python版本不支持内置NotRequired时,分开从typing导入TypedDict、从typing_extensions导入NotRequired——这是官方推荐的过渡方案,完全符合合规性要求,也不会存在词法分析器兼容性问题。
内容的提问来源于stack exchange,提问作者en_Knight
相关产品推荐
相关产品推荐

