注释日期时间对象的推荐方法是什么?

Mar*_*oma 6 datetime type-hinting mypy python-3.8

假设我有一个函数,它需要两个日期时间并以秒为单位返回差值:

import datetime


def diff(d1: datetime.datetime, d2: datetime.datetime) -> float:
    return (d2 - d1).total_seconds()


if __name__ == '__main__':
    d1 = datetime.datetime.now()
    d2 = datetime.datetime.now(datetime.timezone.utc)
    print(diff(d1, d2))
Run Code Online (Sandbox Code Playgroud)

mypy 告诉我这很好:

$ python3.8 -m mypy test.py
Success: no issues found in 1 source file
Run Code Online (Sandbox Code Playgroud)

但我得到一个类型错误:

TypeError: can't subtract offset-naive and offset-aware datetimes
Run Code Online (Sandbox Code Playgroud)

错误消息中明确说明了原因。类型注释不够好。

我想这是一个很常见的情况。是否有推荐的方法来注释时区感知与不感知日期时间对象,从而正确使用 mypy?

Mar*_*oma 6

一种可能的解决方案是使用 TypeVar、NewType 和强制转换:

import datetime
from typing import NewType, TypeVar, cast

NDT = NewType("NDT", datetime.datetime)  # non-aware datetime
ADT = NewType("ADT", datetime.datetime)  # timezone aware datetime
DatetimeLike = TypeVar("DatetimeLike", NDT, ADT)


def diff(d1: DatetimeLike, d2: DatetimeLike) -> float:
    return (d2 - d1).total_seconds()


if __name__ == "__main__":
    d1: NDT = cast(NDT, datetime.datetime.now())
    d2: ADT = cast(ADT, datetime.datetime.now(datetime.timezone.utc))

    # Fails with:
    #    error: Value of type variable "DatetimeLike" of "diff" cannot be "datetime"
    # You have to use either NDT or ADT
    print(diff(d1, d2))
Run Code Online (Sandbox Code Playgroud)

我不喜欢您必须使用的这个解决方案,cast并且错误消息不像以前那么清晰。

  • 您可以通过执行“NDT(datetime.datetime.now())”来避免强制转换。NDT 在运行时只是身份函数,但在类型检查时充当类型签名为“def (val: datetime.datetime) -> NDT”的构造函数。 (6认同)