为什么 mypy 不认为一个类具有 __len__ 和 __getitem__ 但没有 __iter__ 是可迭代的

use*_*151 8 python mypy

我正在尝试mypyPython 中的一些基本迭代,并编写了以下代码库:

from typing import Iterator
from datetime import date, timedelta

class DateIterator:
    def __init__(self, start_date, end_date):
        self.start_date = start_date
        self.end_date = end_date
        self._total_dates = self._get_all_dates()

    def _get_all_dates(self) -> Iterator[date]:
        current_day = self.start_date
        while current_day <= self.end_date:
            yield current_day
            current_day += timedelta(days=1)

    def __len__(self):
        print("Calling the len function...")
        return len(self._total_dates)

    def __getitem__(self, index):
        print(f"Calling getitem with value of index as {index}")
        return self._total_dates[index]

if __name__ == "__main__":
    date_iterator = DateIterator(date(2019, 1, 1), date(2019, 1, 15))
    for new_date in date_iterator:
        print(new_date)

    date_str = ",".join([str(new_date) for new_date in date_iterator])
    print(date_str)

    print(f"Checking the length of the collection {len(date_iterator)}")

    print(f"Checking if indexing works : {date_iterator[4]}")
Run Code Online (Sandbox Code Playgroud)

现在我也遇到mypy了以下问题:

iterator_test_with_getitem.py:30: error: Cannot assign to a type
iterator_test_with_getitem.py:30: error: "DateIterator" has no attribute "__iter__" (not iterable)
iterator_test_with_getitem.py:33: error: "DateIterator" has no attribute "__iter__" (not iterable)
Found 3 errors in 1 file (checked 1 source file)
Run Code Online (Sandbox Code Playgroud)

有人可以指导我,如果一个对象可以通过添加__len__和方法进行迭代,那么当它没有方法时__getitem__为什么会抱怨mypyiter

另外有人可以告诉我第 30 行的问题是什么吗?我也没有找到该错误的任何逻辑解释。

Mic*_*x2a 6

Mypy(以及一般的 PEP 484 类型检查器)将可迭代定义为定义该__iter__方法的任何类。您可以在标准库的类型提示集合 Typeshed 中看到 Iterable 类型的确切定义:https://github.com/python/typeshed/blob/master/stdlib/3/typing.pyi#L146

之所以这样定义Iterable并排除只定义__getitem__and的类型__len__是因为:

  1. 说类型必须实现__iter__或实现的唯一方法__getitem__/__len__是使用 Union——而将 Iterable 定义为 union 会使任何想要在自己的代码中广泛使用 Iterable 类型的人的生活变得有点复杂。

  2. __getitem__相反,对于定义__len__自己的方法的类来说,这是微不足道的__iter__。例如,您可以做这样简单的事情:

    def __iter__(self) -> Iterator[date]:
        for i in range(len(self)):
            yield self[i]
    
    Run Code Online (Sandbox Code Playgroud)

    或者,类似这样的东西(假设你修复了构造函数,所以self._total_dates是列表,而不是生成器):

    def __iter__(self) -> Iterator[date]:
        return iter(self._total_dates)
    
    Run Code Online (Sandbox Code Playgroud)

因此,考虑到这种成本效益权衡,将 Iterable 定义为任何实现__iter__. 对于定义自定义类的人来说,这并不是太大的负担,并且对于想要编写操作可迭代对象的函数的人来说,它简化了生活。