如果 mypy 有 __len__ 和 __getitem__ 但没有 __iter__,为什么 mypy 不认为它是可迭代的
Why does mypy not consider a class as iterable if it has __len__ and __getitem__ but no __iter__
我在玩 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 行的问题是什么。我也没有找到该错误的任何合乎逻辑的解释。
Mypy——和一般的 PEP 484 类型检查器——将可迭代定义为定义 __iter__
方法的任何 class。你可以在标准库的类型提示集合Typeshed中看到Iterable类型的确切定义:https://github.com/python/typeshed/blob/master/stdlib/3/typing.pyi#L146
之所以这样定义Iterable,排除只定义__getitem__
和__len__
的类型是因为:
说一个类型必须实现 __iter__
或实现 __getitem__/__len__
的唯一方法是使用 Union —— 将 Iterable 定义为 union 会使生活复杂化适合任何想在自己的代码中广泛使用 Iterable 类型的人。
相反,定义__getitem__
和__len__
的class定义自己的__iter__
方法是微不足道的。例如,您可以执行如下简单的操作:
def __iter__(self) -> Iterator[date]:
for i in range(len(self)):
yield self[i]
或者,像这样的东西(假设你修复了你的构造函数所以 self._total_dates
是一个列表,而不是一个生成器):
def __iter__(self) -> Iterator[date]:
return iter(self._total_dates)
因此,鉴于这种成本效益权衡,将 Iterable 定义为实现 __iter__
的任何对象是有意义的。对于定义自定义 classes 的人来说,这不是什么负担,对于想要编写函数来操作可迭代对象的人来说,这会简化生活。
我在玩 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 行的问题是什么。我也没有找到该错误的任何合乎逻辑的解释。
Mypy——和一般的 PEP 484 类型检查器——将可迭代定义为定义 __iter__
方法的任何 class。你可以在标准库的类型提示集合Typeshed中看到Iterable类型的确切定义:https://github.com/python/typeshed/blob/master/stdlib/3/typing.pyi#L146
之所以这样定义Iterable,排除只定义__getitem__
和__len__
的类型是因为:
说一个类型必须实现
__iter__
或实现__getitem__/__len__
的唯一方法是使用 Union —— 将 Iterable 定义为 union 会使生活复杂化适合任何想在自己的代码中广泛使用 Iterable 类型的人。相反,定义
__getitem__
和__len__
的class定义自己的__iter__
方法是微不足道的。例如,您可以执行如下简单的操作:def __iter__(self) -> Iterator[date]: for i in range(len(self)): yield self[i]
或者,像这样的东西(假设你修复了你的构造函数所以
self._total_dates
是一个列表,而不是一个生成器):def __iter__(self) -> Iterator[date]: return iter(self._total_dates)
因此,鉴于这种成本效益权衡,将 Iterable 定义为实现 __iter__
的任何对象是有意义的。对于定义自定义 classes 的人来说,这不是什么负担,对于想要编写函数来操作可迭代对象的人来说,这会简化生活。