Ole*_*zov 5 types python-3.x mypy
主要区别是什么?各自应该使用什么以及在哪里使用?
例如,在这个示例中,使用 - 以及迭代器和生成器似乎对我来说是合适的......但这是真的吗?
迭代器
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)])
Run Code Online (Sandbox Code Playgroud)
发电机
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)])
Run Code Online (Sandbox Code Playgroud)
每当您不确定某些内置类型到底是什么时,我建议您检查Typeshed,它是 Python 标准库(以及一些选定的第 3 方模块)的类型提示存储库。Mypy 在每个版本中都会包含一个 typeshed 版本。
例如,以下是迭代器和生成器的定义:
@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]: ...
Run Code Online (Sandbox Code Playgroud)
请注意:
__next__和 ,__iter__但生成器有更多方法。但这在高层上意味着什么呢?
嗯,简而言之,对于迭代器,信息流是单向的。当你有一个迭代器时,你真正能做的就是调用该__next__方法来获取下一个要产生的值。
相反,生成器的信息流是双向的:您可以通过该方法将信息发送回生成器send。
实际上,这就是其他两个类型参数的用途 - 当您这样做时Generator[A, B, C],您是在声明您生成的值是 type A,您发送到生成器的值是 type B,以及您从生成器返回的值属于 类型C。
这里有一些额外有用的阅读材料:
那么,什么时候应该使用迭代器和生成器呢?
好吧,一般来说,您应该偏向于使用有助于调用者了解您期望如何使用返回值的类型。
举个例子,拿你的fib例子来说。您所做的只是产生值:信息流是单向的,并且代码并未真正设置为接受来自调用者的信息。
因此,在这种情况下,使用 Iterator 而不是 Generator 是最容易理解的:Iterator 最能反映 fib 实现的单向性质。
(如果您编写了一个数据流是双向的生成器,那么您当然需要使用生成器而不是迭代器。)
| 归档时间: |
|
| 查看次数: |
2657 次 |
| 最近记录: |