如果一个类有__len__和__getitem__而没有__iter__,为什么mypy不认为它是可迭代的呢?

问题描述 投票:2回答:1

我在玩 mypy 和一些基本的Python迭代,写出了下面的代码库。

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]}")

现在也要玩玩 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)

谁能告诉我,如果一个对象是可迭代的,并添加了 __len____getitem__ 方法,那么为什么 mypy 怨天尤人 iter 办法

另外,谁能告诉我第30行的问题是什么? 我也没有找到任何关于这个错误的逻辑解释。

python mypy
1个回答
2
投票

Mypy -- 以及一般的 PEP 484 类型检查器 -- 定义一个可迭代的类是任何定义了 __iter__ 方法。你可以在Typeshed中看到Iterable类型的确切定义,Typeshed是标准库的类型提示集合。https:/github.pythontypeshedblobmasterstdlib3typing.pyi#L146。

之所以用这种方式定义Iterable,并排除只定义了 __getitem____len__ 就是因为。

  1. 唯一的方法是说,一个类型必须要实现 __iter__ 或实施 __getitem__/__len__ 是使用一个联合体 -- 而定义 Iterable 为联合体,对于任何想在自己的代码中广泛使用 Iterable 类型的人来说,都会使生活变得有点复杂。

  2. 反过来说,对于一个定义了 __getitem____len__ 确定自己的 __iter__ 方法。例如,你可以做一些像这样简单的事情。

    def __iter__(self) -> Iterator[date]:
        for i in range(len(self)):
            yield self[i]
    

    或者像这样 (假设你固定了你的构造函数,所以) self._total_dates 是一个 list,而不是一个生成器)。)

    def __iter__(self) -> Iterator[date]:
        return iter(self._total_dates)
    

所以,考虑到这种成本效益的权衡, 把Iterable定义为任何实现了 __iter__. 对于定义自定义类的人来说,这并不是什么负担,而且对于想写操作迭代表的函数的人来说,也简化了生活。

© www.soinside.com 2019 - 2024. All rights reserved.