Python动态构造NamedTuple类型的函数类型标注问题
动态构造NamedTuple类型的类型标注问题
在Python 3.11(搭配mypy 1.7.0)环境下,我实现了一个动态构造NamedTuple类型的函数,该函数返回构造后的类型而非实例。
最初尝试用Type[NamedTuple]做类型标注,但这会让Python期望NamedTuple构造器本身,而非它的子类。于是我定义了如下协议:
from typing import Protocol, Sequence class PNamedTuple(Protocol): _fields: Sequence[str]
并将Type[PNamedTuple]作为函数的返回类型,这样确实能正常动态创建namedtuple类,但访问_fields时会触发错误:
"_fields" is protected and used outside of the class in which it is declared
问题
- 能否抑制该错误?NamedTuple子类本身不会出现这个问题,如何复现这种行为?
- 是否有简便方式继承其他协议来表达“类元组”行为?
PNamedTuple(Sequence, Protocol)不可行,因为Sequence不是协议,有没有简便的解决方法?
更新补充
- NamedTuple并非类,因此
Type[NamedTuple]的用法本身是错误的(参考@blhsing的已采纳答案)。 - 正如@dROOOze指出,
_fields的错误来自pylance(推测源于pyright),该解决方案会因同一原因触发错误,但mypy可以正常处理,且无需使用协议类。
解答
问题1:抑制_fields的保护成员访问错误
- 针对mypy:在访问
_fields的代码行末尾添加# type: ignore[protected-access]注释,即可单独抑制该错误。 - 针对pylance/pyright:
- 可在协议类中添加
__allow_untyped_access__ = True类属性,或者在pyrightconfig.json中配置"reportProtectedAccess": false全局关闭该检查,也可在目标文件顶部添加# pyright: reportProtectedAccess=false注释仅针对当前文件生效。
- 可在协议类中添加
- 复现NamedTuple子类的无报错行为:无需自定义协议,直接用
TypeVar绑定到NamedTuple即可,示例代码:
此时访问返回类型的from typing import NamedTuple, TypeVar, Type from collections import namedtuple T = TypeVar('T', bound=NamedTuple) def create_dynamic_namedtuple() -> Type[T]: return namedtuple("DynamicNT", ["field1", "field2"])_fields属性,mypy和pylance都不会报错。
问题2:表达“类元组”行为的简便方式
- 直接继承
collections.abc.Sequence协议:collections.abc.Sequence本身就是协议类型,可直接让自定义协议继承它,示例:from collections.abc import Sequence from typing import Protocol class PNamedTuple(Sequence, Protocol): _fields: Sequence[str] - 利用
TypeVar绑定NamedTuple:所有NamedTuple子类天然实现了序列协议,用TypeVar('T', bound=NamedTuple)约束返回类型后,返回的类型自动具备元组的所有行为,无需额外继承协议。
内容的提问来源于stack exchange,提问作者shaunc
相关产品推荐
相关产品推荐

