Mypy 迭代器和生成器之间有什么区别?
What's the difference between Mypy iterators and generators?
主要区别是什么?应该在什么地方使用它们?
例如,在此示例中同时使用 - 以及 Iterator 和 Generator 对我来说似乎很合适...但这是真的吗?
迭代器
from typing import Generator, Iterator
def fib(n: int) -> Iterator[int]:
a :int = 0
b :int = 1
while a < n:
yield a
a, b = b, a+b
print([x for x in fib(3)])
发电机
from typing import Generator
def fib(n: int) -> Generator[int, None, None]:
a :int = 0
b :int = 1
while a < n:
yield a
a, b = b, a+b
print([x for x in fib(3)])
每当您不确定某些内置类型到底是什么时,我建议您检查 Typeshed,Python 标准库(以及某些 select 3rd 的类型提示库)派对模块)。 Mypy 在每个版本中都包含一个 typeshed 版本。
例如,here are the definitions 迭代器和生成器究竟是什么:
@runtime
class Iterator(Iterable[_T_co], Protocol[_T_co]):
@abstractmethod
def __next__(self) -> _T_co: ...
def __iter__(self) -> Iterator[_T_co]: ...
class Generator(Iterator[_T_co], Generic[_T_co, _T_contra, _V_co]):
@abstractmethod
def __next__(self) -> _T_co: ...
@abstractmethod
def send(self, value: _T_contra) -> _T_co: ...
@abstractmethod
def throw(self, typ: Type[BaseException], val: Optional[BaseException] = ...,
tb: Optional[TracebackType] = ...) -> _T_co: ...
@abstractmethod
def close(self) -> None: ...
@abstractmethod
def __iter__(self) -> Generator[_T_co, _T_contra, _V_co]: ...
@property
def gi_code(self) -> CodeType: ...
@property
def gi_frame(self) -> FrameType: ...
@property
def gi_running(self) -> bool: ...
@property
def gi_yieldfrom(self) -> Optional[Generator]: ...
注意:
- 迭代器只有两种方法:
__next__
和 __iter__
但生成器有更多方法。
- 生成器是迭代器的 子类型 -- 每个生成器也是迭代器,但 vice-versa 不是。
但这在 high-level 上意味着什么?
嗯,简而言之,有了迭代器,信息流只有one-way。当你有一个迭代器时,你真正能做的就是调用 __next__
方法来获取下一个要产生的值。
相比之下,生成器的信息流是双向:您可以通过[=14]将信息返回发送到生成器=] 方法。
实际上,这就是其他两个类型参数的用途——当您执行 Generator[A, B, C]
时,您是在声明您生成的值属于 A
类型,即您发送到的值生成器的类型为 B
,您从生成器中 return 的值的类型为 C
。
这里有一些额外的有用读物 material:
- python generator "send" function purpose?
- Difference between Python's Generators and Iterators
- Return in generator together with yield in Python 3.3
- https://jeffknupp.com/blog/2013/04/07/improve-your-python-yield-and-generators-explained/
那么,什么时候应该使用 Iterator 与 Generator?
一般来说,您应该偏向于使用有助于调用者理解您期望如何使用 return 值的类型。
例如,以您的fib
为例。您所做的一切就是产生值:信息流是 one-way,并且代码并未真正设置为接受来自调用者的信息。
因此,在这种情况下使用迭代器而不是生成器是最容易理解的:迭代器最能反映您的 fib 实现的 one-way 性质。
(如果您编写了一个双向数据流的生成器,您当然需要使用生成器而不是迭代器。)
主要区别是什么?应该在什么地方使用它们?
例如,在此示例中同时使用 - 以及 Iterator 和 Generator 对我来说似乎很合适...但这是真的吗?
迭代器
from typing import Generator, Iterator
def fib(n: int) -> Iterator[int]:
a :int = 0
b :int = 1
while a < n:
yield a
a, b = b, a+b
print([x for x in fib(3)])
发电机
from typing import Generator
def fib(n: int) -> Generator[int, None, None]:
a :int = 0
b :int = 1
while a < n:
yield a
a, b = b, a+b
print([x for x in fib(3)])
每当您不确定某些内置类型到底是什么时,我建议您检查 Typeshed,Python 标准库(以及某些 select 3rd 的类型提示库)派对模块)。 Mypy 在每个版本中都包含一个 typeshed 版本。
例如,here are the definitions 迭代器和生成器究竟是什么:
@runtime
class Iterator(Iterable[_T_co], Protocol[_T_co]):
@abstractmethod
def __next__(self) -> _T_co: ...
def __iter__(self) -> Iterator[_T_co]: ...
class Generator(Iterator[_T_co], Generic[_T_co, _T_contra, _V_co]):
@abstractmethod
def __next__(self) -> _T_co: ...
@abstractmethod
def send(self, value: _T_contra) -> _T_co: ...
@abstractmethod
def throw(self, typ: Type[BaseException], val: Optional[BaseException] = ...,
tb: Optional[TracebackType] = ...) -> _T_co: ...
@abstractmethod
def close(self) -> None: ...
@abstractmethod
def __iter__(self) -> Generator[_T_co, _T_contra, _V_co]: ...
@property
def gi_code(self) -> CodeType: ...
@property
def gi_frame(self) -> FrameType: ...
@property
def gi_running(self) -> bool: ...
@property
def gi_yieldfrom(self) -> Optional[Generator]: ...
注意:
- 迭代器只有两种方法:
__next__
和__iter__
但生成器有更多方法。 - 生成器是迭代器的 子类型 -- 每个生成器也是迭代器,但 vice-versa 不是。
但这在 high-level 上意味着什么?
嗯,简而言之,有了迭代器,信息流只有one-way。当你有一个迭代器时,你真正能做的就是调用 __next__
方法来获取下一个要产生的值。
相比之下,生成器的信息流是双向:您可以通过[=14]将信息返回发送到生成器=] 方法。
实际上,这就是其他两个类型参数的用途——当您执行 Generator[A, B, C]
时,您是在声明您生成的值属于 A
类型,即您发送到的值生成器的类型为 B
,您从生成器中 return 的值的类型为 C
。
这里有一些额外的有用读物 material:
- python generator "send" function purpose?
- Difference between Python's Generators and Iterators
- Return in generator together with yield in Python 3.3
- https://jeffknupp.com/blog/2013/04/07/improve-your-python-yield-and-generators-explained/
那么,什么时候应该使用 Iterator 与 Generator?
一般来说,您应该偏向于使用有助于调用者理解您期望如何使用 return 值的类型。
例如,以您的fib
为例。您所做的一切就是产生值:信息流是 one-way,并且代码并未真正设置为接受来自调用者的信息。
因此,在这种情况下使用迭代器而不是生成器是最容易理解的:迭代器最能反映您的 fib 实现的 one-way 性质。
(如果您编写了一个双向数据流的生成器,您当然需要使用生成器而不是迭代器。)