簡體   English   中英

mypy:如何讓它正確猜測類型?

[英]mypy: how to make it correctly guess the type?

考慮下面的例子

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>

除了類型提示變量本身之外,有沒有辦法讓它 mypy 正確檢測這里的類型?

是的,有附帶條件。 重載解決方案正在推動 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 在對函數內部進行類型檢查時不考慮可用的重載。 因此,作為人類讀者,我們可以得出結論,在if語句中,我們處於重載 1 中,但 mypy 仍在考慮container可以是任何Optional[Callable[[Iterable[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, builtins.int] . So a union of . So a union of int and an unsolved metavariable. Lovely. I suspect this has to do with the fact that and an unsolved metavariable. Lovely. I suspect this has to do with the fact that 我懷疑這與 sum` 有幾個通用重載and an unsolved metavariable. Lovely. I suspect this has to do with the fact that並且可能只是混淆了 mypy。 同樣,我們可以使用顯式 lambda 繞過它。

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

所以答案是肯定的,如果你願意通過在任何地方慷慨地插入lambda來幫助類型檢查器。 但是這種事情把 mypy 推到了極限。 我不確定其他類型檢查器在這種情況下是否會做更好的通用推理,因為我最熟悉 mypy。 如果有人用另一個 Python 類型檢查器嘗試過這個,請隨時發布您的結果。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM