微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

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

我正在自学 Python 并试图通过 mypy 的类型检查系统,但我有点迷失在类型、类、抽象类、泛型等之间。

所以,我想制作一个泛型/抽象类型/类来表示日期,指定该类型/类必须具有 yearmonthday 属性并且必须支持比较运算符 <<=,可能还有其他方法。我知道这听起来确实像是抽象类的工作,但我不精通 OO,而且我之前尝试使用抽象类未能通过 mypy 的类型检查。基于通用/抽象模式,我定义了一个将 n 个月添加到日期的函数。接下来,我想根据模块 datetime 中的日期类型定义一个特定/具体的日期类型,并且我重新定义了 add_months 函数以使用该类型进行操作,但这个想法当然是调用为泛型/抽象模式编写的函数,不要重复代码

希望下面的代码能让我的意图更清晰(我的代码被分成两个文件):

文件dates_generic.py:

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)

文件dates_pylib.py:


import calendar as cal

import dates_generic as dg

import datetime as dt

from dates_generic import Year,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,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)

我的问题是 mypy 仍然使用我的函数 dates_pylib.add_months 提出以下问题:

Value of type variable "Date" of "add_months" cannot be "DateP"

我用作 IDE 的 PyCharm 添加了自己的:

Expected type '(int,int,int) -> Any' (matched generic type '(int,int) -> Date'),got '(y: int,m: int,d: int) -> DateP' instead

从第一条消息中,我似乎明白 mypy 不知道类型 DateP 实现了年、月和日属性和比较运算符。如果我删除了dates_generic.py 中的bound=A,此消息就会消失,但随后mypy 抱怨无界类型Date 没有yearmonthday属性

第二条消息对我来说意义不大,因为我从 PEP-0484 中读到“每种类型都与 Any 一致”,所以我希望 (int,int) -> Any 可以替换为 (int,int) -> DateP

我可能正在寻找类似于 Haskell 的类型类约束的东西,它允许您指定类型必须支持方法,但我不确定如何在 Python 中模拟它。

解决方法

听起来您想使用协议(昵称为“静态鸭子类型”)来描述您的 Date 实现必须具有的功能。协议完全是另一种打字的东西,一次学习所有这些东西肯定有很多,但是一旦你习惯了它们,它们真的很好。

定义协议告诉类型检查器,“如果你发现任何可以完成所有这些事情的东西,它就是这个协议的实现者,这意味着期望这个协议的实现者的人应该对它感到满意”。它很像一个抽象基类,但也不是——它只是一个接口规范,实际上没有人需要继承它(无论如何,从协议继承基本上是一种无操作——mypy 会弄清楚是否有任何类实现一个协议,无论您是否声明该类继承自该协议)。您可以阅读有关协议 here 的 PEP。

您可能不需要它,或者可能已经找到它,但由于听起来您是在尝试描述和重新实现 datetime 功能的子集,因此 datetime type stubs 可能有用也给你。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。