首页 > 编程问答 >Python:在 Protocol 和 TypedDict 之间共享类型注释

Python:在 Protocol 和 TypedDict 之间共享类型注释

时间:2024-07-30 13:57:25浏览次数:16  
标签:python python-typing mypy

举这个简单的例子:

from __future__ import annotations
import typing as t


class MyType:
    def __init__(self, s: str, i: int) -> None:
        self.s = s
        self.i = i


class MyProto(t.Protocol):
    s: str
    i: int


class MyDict(t.TypedDict):
    s: str
    i: int


def my_serializer(inst: MyProto) -> MyDict:
    return {"s": inst.s, "i": inst.i}


d = my_serializer(MyType("a", 1))

所有类型检查都通过。

现在我们可以说 MyType 实际上是一个具有许多属性的 ORM 类,它是协议和字典类型的真实来源。每次向类中添加属性时,都必须在 Protocol 类主体和 TypedDict 类主体中维护相同的注释,感觉有点多余。

我想知道是否有一种方法可以集中定义类型注释并告诉 mypy 这些是协议和 dict 类的类型定义。

我尝试了这个:

class TypeMixin:
    s: str
    i: int


class MyProto(TypeMixin, t.Protocol):
    pass


class MyDict(TypeMixin, t.TypedDict):
    pass

但是,mypy 抱怨:

test.py:15: error: All bases of a protocol must be protocols
test.py:19: error: All bases of a new TypedDict must be TypedDict types

...而这实际上是一个 TypeError运行时。

这个:

annos = {"s": "str", "i": "int"}
MyProto = type("MyProto", (t.Protocol,), {"__annotations__": annos})
MyDict = type("MyDict", (t.TypedDict,), {"__annotations__": annos})


def my_serializer(inst: MyProto) -> MyDict:
    return {"s": inst.s, "i": inst.i}

这个运行,但 mypy 抱怨,我认为这对于 mypy 来说有点太动态了:

test.py:12: error: Argument 2 to "type" has incompatible type "Tuple[_SpecialForm]"; expected "Tuple[type, ...]"
test.py:13: error: Argument 2 to "type" has incompatible type "Tuple[object]"; expected "Tuple[type, ...]"
test.py:16: error: Variable "topsport.events.test.MyProto" is not valid as a type
test.py:16: error: Variable "topsport.events.test.MyDict" is not valid as a type
test.py:17: error: MyProto? has no attribute "s"
test.py:17: error: MyProto? has no attribute "i"

我想做的事情是不可能的吗?


你想要实现的目标是绝对有可能的! 你面临的问题是 typing.Protocol typing.TypedDict 的性质。 它们是特殊的类型,对其基类有限制。

让我们探讨一下如何使用以下策略实现你的目标:

  1. 使用类装饰器:

```python from future import annotations import typing as t

def add_shared_annotations(cls): cls. annotations = {"s": str, "i": int} return cls

@add_shared_annotations class MyProto(t.Protocol): pass

@add_shared_annotations class MyDict(t.TypedDict): pass

class MyType: def init (self, s: str, i: int) -> None: self.s = s self.i = i

def my_serializer(inst: MyProto) -> MyDict: return {"s": inst.s, "i": inst.i}

d = my_serializer(MyType("a", 1)) ```

在这个例子中,我们创建了一个装饰器 add_shared_annotations ,它接受一个类并添加共享的注解。 这让你可以在一个位置定义注解,并将其应用于 MyProto MyDict

  1. 使用帮助函数生成类型:

```python from future import annotations import typing as t

def create_shared_types() -> tuple[t.Type[t.Protocol], t.Type[t.TypedDict]]: shared_annotations = {"s": str, "i": int} MyProto = type("MyProto", (t.Protocol,), shared_annotations) MyDict = type("MyDict", (t.TypedDict,), shared_annotations) return MyProto, MyDict

MyProto, MyDict = create_shared_types()

class MyType: def init (self, s: str, i: int) -> None: self.s = s self.i = i

def my_serializer(inst: MyProto) -> MyDict: return {"s": inst.s, "i": inst.i}

d = my_serializer(MyType("a", 1)) ```

这里,我们使用 create_shared_types 函数在运行时动态生成 MyProto MyDict 类型。 虽然这避免了重复,但它可能比类装饰器方法更难理解。

为你的用例选择最佳方法取决于你的偏好和项目的具体需求。 两种方法都允许你集中定义类型注释并在 Protocol TypedDict 之间共享,同时保持 mypy 的类型安全性。

标签:python,python-typing,mypy
From: 60348451

相关文章

  • PIL 和 python 静态类型
    我有一个函数参数,它可以接受图像的多种类型:defsomefunc(img:Union[np.array,Image,Path,str]):PILImage在这种情况下抛出以下异常:TypeError:Union[arg,...]:eachargmustbeatype.Got<module'PIL.Image'from...进一步检查图像对象后这才有......
  • 学会用Python爬取小说网站,想看什么就爬什么,广告也不用看了~
    今天以爬取笔趣阁小说网站为例,练习Python爬虫技术。通过这个爬虫,可以完成批量爬取一本小说的所有章节,并将所有章节内容按顺序保存到一个txt文档内,下面我们就开始吧。首先,百度搜索“笔趣阁”,发现有很多网站都叫笔趣阁。我们可以随便挑选一个网站尝试,本文我以‘https://......
  • 计算机毕业设计django+vue《Python数据分析》的教学系统【开题+论文+程序】
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景在当今数字化时代,数据分析已成为各行各业不可或缺的技能之一,而Python作为数据分析领域的首选语言,其重要性日益凸显。然而,传统的教学模式在......
  • 如何将多个变量分配给 python 函数中的单个参数?
    我正在尝试编写一个程序,如果可能的话,它需要一个三项式并对其进行因式分解。每当用户输入A、B和C时,三项式应该通过Factor(product,summation)函数获取,但我似乎无法弄清楚如何将A和C分配给乘积arg,将B分配给我尝试在函数外部声明不同的变量,product=(a*c)和summati......
  • python - 从文本生成音乐
    请给我一些建议为了解释一下,我输入“深度睡眠的睡眠音乐”,它将返回一个wav文件:https://www.youtube.com/watch?v=1wAdQhFJy54或者我给出一个wav文件,它会返回相同的现在这是我尝试过的:https://github.com/facebookresearch/audiocraft......
  • 当 functools.wraps() 用于泛型函数时,Mypy 1.10 报告错误
    TLDR;我有一个装饰器:更改函数签名包装的函数使用一些泛型类型参数除了我想使用的签名funtools.wraps以保留其余部分信息。有什么办法可以在不抱怨的情况下实现这一目标吗?mypy更多背景一个最小的工作示例如下所示:这......
  • 从零开始的Python开发日记(7):短信验证功能开发流程
    短信验证功能开发流程在开发一个包含登录、注册以及短信验证的功能时,你需要遵循一个系统的开发流程。以下是实现这一功能的基本步骤,包括所需的技术和代码示例。1.环境配置首先,确保你的开发环境已经配置好,并安装了必要的库和工具。pipinstallfastapiuvicornsqlalche......
  • 【Python数值分析】革命:引领【数学建模】新时代的插值与拟合前沿技术
    目录​编辑第一部分:插值的基本原理及应用1.插值的基本原理1.1插值多项式1.2拉格朗日插值 1.3牛顿插值 1.4样条插值2.插值的Python实现2.1使用NumPy进行插值2.2使用SciPy进行插值2.2.1一维插值​编辑2.2.2二维插值3.插值的应用场景3.1数据平......
  • 在家用电脑上设置 Python 和 Jupyter,尝试打开 Jupyter 笔记本并显示错误,无法获取
    我有最新的Python版本3.12.4和以下版本的Jupyter:SelectedJupytercorepackages...IPython:8.26.0ipykernel:6.29.5ipywidgets:notinstalledjupyter_client:8.6.2jupyter_core:5.7.2jupyter_server:2.14.2jupyterlab......
  • Python - Reloading a module
    Eachmoduleisloadedintomemoryonlyonceduringaninterpretersessionorduringaprogramrun,regardlessofthenumberoftimesitisimportedintoaprogram.Ifmultipleimportsoccur,themodule’scodewillnotbeexecutedagainandagain.Suppose......