如何让mypy知道现有的类型支持某些属性和方法?

8 浏览
0 Comments

如何让mypy知道现有的类型支持某些属性和方法?

我正在自学Python,试图逐步理解mypy的类型检查系统,但在类型、类、抽象类、泛型类型等概念中有些迷茫。\n因此,我想创建一个泛型/抽象类型/类来表示日期,指定该类型/类必须具有“年”、“月”和“日”的属性,并且必须支持比较运算符“<”和“<=”,可能还要支持其他方法。我知道这听起来确实适合使用抽象类,但我对面向对象不熟练,我之前尝试使用抽象类未能通过mypy的类型检查。基于泛型/抽象模式,我定义了一个函数,用于在日期上添加n个月。接下来,我想基于模块“datetime”的日期类型定义一个特定/具体的日期类型,并重新定义“add_months”函数以操作该类型,但当然是要调用为泛型/抽象模式编写的函数,而不是复制代码。\n希望下面的代码能更清楚地表达我的意图(我的代码分为两个文件):\n文件dates_generic.py:\n[apcode language="python"]

from typing import Callable, TypeVar
Year = int
Month = int
Day = int
class A:
    year: Year
    month: Month
    day: Day
    def __lt__(self: A, other: A) -> bool:
        ...
    def __le__(self: A, other: A) -> bool:
        ...
Date = TypeVar('Date', bound=A)
def add_months(x: Date,
               n: int,
               days_in_month: Callable[[Year, Month], int],
               date_make: Callable[[Year, Month, Day], Date]) -> Date:
    r = (x.month + n - 1) % 12
    q = (x.month + n - 1) // 12
    y = x.year + q
    m = r + 1
    d = min(x.day, days_in_month(y, m))
    return date_make(y, m, d)

[/apcode]\n文件dates_pylib.py:\n

import calendar as cal
import dates_generic as dg
import datetime as dt
from dates_generic import Year, Month, Day
from typing import NewType
DateP = NewType('DateP', dt.date)
def date_make(y: Year, m: Month, d: Day) -> DateP:
    return DateP(dt.date(y, m, d))
def days_in_month(y: Year,
                  m: Month):
    return cal.monthrange(y, m)[1]
def add_months(x: DateP,
               n: int) -> DateP:
    return dg.add_months(x, n, days_in_month, date_make)

\n我的问题是,mypy仍然对我的函数`dates_pylib.add_months`提出以下问题:\n

类型变量"Date"的值不能为"DateP"

\n而我使用的IDE PyCharm还添加了以下提示:\n预期类型“(int, int, int) -> Any”(匹配的泛型类型“(int, int, int) -> Date”),得到的却是“(y: int, m: int, d: int) -> DateP”\n\n根据第一条消息,我似乎理解mypy并不知道类型`DateP`实现了年、月和日的属性以及比较运算符。如果我在`dates_generic.py`中删除`bound=A`,这条消息就会消失,但是mypy会抱怨无界类型`Date`没有`year`、`month`和`day`属性。\n第二条消息对我来说不太明白,因为我从PEP-0484中读到“每种类型都与Any类型兼容”,所以我期望`(int, int, int) -> Any`可以替换为`(int, int, int) -> DateP`。\n我可能正在寻找类似于Haskell的类型类约束的东西,它允许您指定类型必须支持的方法,但我不确定如何在Python中模拟这个。

0
0 Comments

如何让mypy知道现有类型支持某些属性和方法?

问题的出现原因:用户想要描述自己的Date实现必须具备的功能,使用Protocol(通俗地称为“静态鸭子类型”)。Protocol是一种全新的类型定义方式,一下子要学习这么多东西确实很多,但一旦你习惯了它们,就会发现它们非常好用。

解决方法:定义一个Protocol,告诉类型检查器“如果你找到任何实现了这些功能的东西,它就是这个Protocol的实现者,这意味着那些期望实现该Protocol的人应该对它满意”。它很像抽象基类,但又不完全一样——它只是一个接口规范,实际上没人需要从它继承(而且从Protocol继承基本上是一个空操作——mypy会确定任何类是否实现了Protocol,无论你是否声明该类继承自该Protocol)。你可以在这里阅读有关Protocols的PEP。

你可能不需要这个,或者可能已经找到了,但由于你似乎正试图描述和重新实现datetime的某些功能子集,datetime的类型存根可能也对你有用。

感谢你提供的有用链接。我之前从未听说过Protocols,但标题中的“静态鸭子类型”听起来很有趣,因为我的问题是关于一个已有类型(datetime.date)是否符合我定义的模式。看来在修复我的代码之前,我有一些阅读材料要研究,但你给了我一个有希望的方向。

0