簡體   English   中英

通用* args(特別是zip或zipWith)的Python類型提示

[英]Python type hints for generic *args (specifically zip or zipWith)

我正在使用以下簽名編寫一個名為zip_with的函數:

_A = TypeVar("_A")
_B = TypeVar("_B")
_C = TypeVar("_C")


def zip_with(zipper: Callable[[_A, _B], _C], a_vals: Iterable[_A], b_vals: Iterable[_B]) -> Generator[_C, None, None]: ...

就像zip一樣,但是允許您聚合任何任意函數。 這對於僅允許2個參數的zip_with實現zip_with

是否支持為可變數量的參數添加類型提示? 具體來說,我想要一個任意的泛型類型列表,並且希望類型檢查器能夠將參數的類型與zipper的參數進行匹配。 沒有特定類型的方法如下:

def zip_with(zipper: Callable[..., _C], *vals: Iterable) -> Generator[_C, None, None]: ...

換句話說,我希望類型檢查器能夠將*vals的類型與zipper的輸入參數進行匹配。

不幸的是,沒有一種表達這種類型簽名的干凈方法。 為此,我們需要一個稱為可變參數泛型的功能。 雖然加入這個概念PEP 484整體利益,它可能不會在短期內發生。

特別是對於mypy核心團隊,我粗略估計此功能的這項工作可能暫定於今年晚些時候開始,但可能要等到最早的2020年初到中期才能投入使用。 (這是基於與團隊中各個成員的面對面交談。)


當前的解決方法是濫用過載,如下所示:

from typing import TypeVar, overload, Callable, Iterable, Any, Generator

_T1 = TypeVar("_T1")
_T2 = TypeVar("_T2")
_T3 = TypeVar("_T3")
_T4 = TypeVar("_T4")
_T5 = TypeVar("_T5")

_TRet = TypeVar("_TRet")

@overload
def zip_with(zipper: Callable[[_T1, _T2], _TRet], 
             __vals1: Iterable[_T1],
             __vals2: Iterable[_T2],
             ) -> Generator[_TRet, None, None]: ...
@overload
def zip_with(zipper: Callable[[_T1, _T2, _T3], _TRet], 
             __vals1: Iterable[_T1],
             __vals2: Iterable[_T2],
             __vals3: Iterable[_T3],
             ) -> Generator[_TRet, None, None]: ...
@overload
def zip_with(zipper: Callable[[_T1, _T2, _T3, _T4], _TRet], 
             __vals1: Iterable[_T1],
             __vals2: Iterable[_T2],
             __vals3: Iterable[_T3],
             __vals4: Iterable[_T4],
             ) -> Generator[_TRet, None, None]: ...
@overload
def zip_with(zipper: Callable[[_T1, _T2, _T3, _T4, _T5], _TRet], 
             __vals1: Iterable[_T1],
             __vals2: Iterable[_T2],
             __vals3: Iterable[_T3],
             __vals4: Iterable[_T4],
             __vals5: Iterable[_T5],
             ) -> Generator[_TRet, None, None]: ...

# One final fallback overload if we want to handle callables with more than
# 5 args more gracefully. (We can omit this if we want to bias towards
# full precision at the cost of usability.)
@overload
def zip_with(zipper: Callable[..., _TRet],
             *__vals: Iterable[Any],
             ) -> Generator[_TRet, None, None]: ...

def zip_with(zipper: Callable[..., _TRet],
             *__vals: Iterable[Any],
             ) -> Generator[_TRet, None, None]:
    pass

這種方法顯然非常笨拙-編寫起來很笨拙,並且僅對最多接受5個args的可調用對象執行精確的類型檢查。

但是實際上,這通常已經足夠了。 實用上,大多數可調用對象的時間都不太長,如果需要,我們總是可以處理更多的重載以處理更多的特殊情況。

實際上,此技術實際上是用於定義zip類型的技術: https : //github.com/python/typeshed/blob/master/stdlib/2and3/builtins.pyi#L1403

暫無
暫無

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

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