Mypy:如何正确猜测类型?

发布于 2025-02-11 18:59:10 字数 1432 浏览 1 评论 0原文

考虑以下示例

from typing import Callable, Iterable, TypeVar, Any, overload, Tuple, TYPE_CHECKING
from itertools import islice

T = TypeVar("T")

@overload
def mytake(n:int, iterable:Iterable[T]) -> Tuple[T,...]:...
@overload
def mytake(n:int, iterable:Iterable[T], container:Callable[[Iterable[T]],Any]) -> Any:...

def mytake(n:int, iterable:Iterable[T], container:Callable[[Iterable[T]],Any]=tuple) -> Any:
    return container(islice(iterable,n))

if TYPE_CHECKING:
    pass
else:
    reveal_type = print

#a:tuple[int,...]
a = mytake(10,range(100))
reveal_type(a)

#b:list[int]
b = mytake(10,range(100), list)
reveal_type(b)#desired to be list[int]

#c:int
c = mytake(10,range(100),sum)
reveal_type(c)#desired to be int


help(mytake)

(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
45
Help on function mytake in module __main__:

mytake(n: int, iterable: Iterable[~T], container: Callable[[Iterable[~T]], Any] = <class 'tuple'>) -> Any

即基本上是愿望,但是我希望Mypy

C:\Users\copperfield\Desktop\mypy_tests>mypy test6.py
test6.py:21: note: Revealed type is "builtins.tuple[builtins.int, ...]"
test6.py:25: note: Revealed type is "Any"
test6.py:29: note: Revealed type is "Any"
Success: no issues found in 1 source file

C:\Users\copperfield\Desktop\mypy_tests>

在暗示变量本身的类型旁边是一样的,是否有一种方法可以使它如此正确地检测到这里的类型?

consider the following example

from typing import Callable, Iterable, TypeVar, Any, overload, Tuple, TYPE_CHECKING
from itertools import islice

T = TypeVar("T")

@overload
def mytake(n:int, iterable:Iterable[T]) -> Tuple[T,...]:...
@overload
def mytake(n:int, iterable:Iterable[T], container:Callable[[Iterable[T]],Any]) -> Any:...

def mytake(n:int, iterable:Iterable[T], container:Callable[[Iterable[T]],Any]=tuple) -> Any:
    return container(islice(iterable,n))

if TYPE_CHECKING:
    pass
else:
    reveal_type = print

#a:tuple[int,...]
a = mytake(10,range(100))
reveal_type(a)

#b:list[int]
b = mytake(10,range(100), list)
reveal_type(b)#desired to be list[int]

#c:int
c = mytake(10,range(100),sum)
reveal_type(c)#desired to be int


help(mytake)

that ouput

(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
45
Help on function mytake in module __main__:

mytake(n: int, iterable: Iterable[~T], container: Callable[[Iterable[~T]], Any] = <class 'tuple'>) -> Any

Basically just as desire, but I would like that mypy was the same

C:\Users\copperfield\Desktop\mypy_tests>mypy test6.py
test6.py:21: note: Revealed type is "builtins.tuple[builtins.int, ...]"
test6.py:25: note: Revealed type is "Any"
test6.py:29: note: Revealed type is "Any"
Success: no issues found in 1 source file

C:\Users\copperfield\Desktop\mypy_tests>

Beside type hinting the variables themselves, is there a way to make it so mypy correctly detect the types here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

少跟Wǒ拽 2025-02-18 18:59:11

是的,有条件。超负荷分辨率正在推动Mypy已经紧张的功能的边缘,因此我们可以接近。

您想要的是额外的通用,用于可可的返回类型。

T = TypeVar("T")
S = TypeVar("S")

@overload
def mytake(n:int, iterable: Iterable[T]) -> Tuple[T,...]:...
@overload
def mytake(n:int, iterable: Iterable[T], container: Callable[[Iterable[T]],S]) -> S:...

def mytake(n:int, iterable: Iterable[T], container: Optional[Callable[[Iterable[T]],S]] = None) -> S:
    if container is None:
        # We're in overload 1
        return cast(S, tuple(islice(iterable, n)))
    else:
        # We're in overload 2
        return container(islice(iterable, n))

请注意,我们必须在案例1中进行施放,因为Mypy不考虑在功能的内部检查时可用的过载。因此,正如人类读者一样 可选[Callable [[iToble [t]],s]]。这是一个安全的演员,只有一个mypy就没有得到。

这是我们想要的签名,但是可以使用它很尴尬。

#a:tuple[int,...]
a = mytake(10,range(100))
reveal_type(a)

第一个仍然按预期工作。

#b:list[int]
b = mytake(10,range(100), list)
reveal_type(b)#desired to be list[int]

第二个不起作用。实际上,我们会遇到一个错误。 mypy具有一些问题 将类型解释为callable实例通用上下文,因此我们可以使用lambda解决此问题。

#b:list[int]
b = mytake(10,range(100), lambda x: list(x))
reveal_type(b)#desired to be list[int]

不理想,但可行。

第三个不是那么幸运。

#c:int
c = mytake(10,range(100),sum)
reveal_type(c)#desired to be int

老实说,我什至不确定这里会发生什么。 mypy报告c的类型是union [_t -1,inceartins.int] 。因此, int 的结合和无法解决的metavar。迷人的。我怀疑这与以下事实有关: Noreferrer“>几个通用过载,可能只是使Mypy感到困惑。同样,我们可以用明确的lambda解决它。

#c:int
c = mytake(10,range(100),lambda x: sum(x))
reveal_type(c)#desired to be int

因此,答案是肯定的,如果您愿意通过慷慨地插入lambda来帮助类型的检查器。但是这种事情将Mypy推到了极限。我不确定其他类型的检查器在这种情况下是否会做得更好,因为我对Mypy最熟悉。如果有人与另一个Python类型检查器尝试了此操作,请随时发布您的结果。

Yes, with provisos. Overload resolution is pushing on the edge of mypy's already strained capabilities, so we can get close-ish.

What you want is an additional generic, for the return type of the callable.

T = TypeVar("T")
S = TypeVar("S")

@overload
def mytake(n:int, iterable: Iterable[T]) -> Tuple[T,...]:...
@overload
def mytake(n:int, iterable: Iterable[T], container: Callable[[Iterable[T]],S]) -> S:...

def mytake(n:int, iterable: Iterable[T], container: Optional[Callable[[Iterable[T]],S]] = None) -> S:
    if container is None:
        # We're in overload 1
        return cast(S, tuple(islice(iterable, n)))
    else:
        # We're in overload 2
        return container(islice(iterable, n))

Note that we have to cast in Case 1, as mypy doesn't consider the overloads that are available when type-checking the inside of the function. So we, as human readers, can conclude that, inside that if statement, we're in Overload 1, but mypy is still considering that container could be any Optional[Callable[[Iterable[T]], S]]. It's a safe cast, just one mypy doesn't quite get.

This is the signature we want, but it's awkward to work with.

#a:tuple[int,...]
a = mytake(10,range(100))
reveal_type(a)

The first one still works as intended.

#b:list[int]
b = mytake(10,range(100), list)
reveal_type(b)#desired to be list[int]

The second one won't work. We actually get an error. Mypy has some issues with interpreting types as Callable instances in generic contexts, so we can get around this using a lambda.

#b:list[int]
b = mytake(10,range(100), lambda x: list(x))
reveal_type(b)#desired to be list[int]

Not ideal, but workable.

The third one is not so lucky.

#c:int
c = mytake(10,range(100),sum)
reveal_type(c)#desired to be int

To be quite honest, I'm not even sure what happens here. Mypy reports that the type of c is Union[_T-1, builtins.int]. So a union ofintand an unsolved metavariable. Lovely. I suspect this has to do with the fact thatsum` has several generic overloads and is probably just confusing mypy. Again, we can get around it with an explicit lambda.

#c:int
c = mytake(10,range(100),lambda x: sum(x))
reveal_type(c)#desired to be int

So the answer is yes, if you're willing to help the type checker out by generously inserting lambda everywhere. But this sort of thing pushes mypy to its limit. I'm not sure if other type checkers would do better generic inference in this case, as I'm most familiar with mypy. If someone has tried this with another Python type checker, feel free to post your results as well.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文