简体   繁体   English

Python可以打*(拆包)操作符吗? 或者任何其他可变参数 function 使得所有可变参数类型都在结果类型中?

[英]Can the * (unpacking) operator be typed in Python? Or any other variadic args function such that all variadic types are in the result type?

Working with type stubs, I'm wondering if it's possible to express a type in Python that allows you to type this correctly for any number of arguments:使用类型存根,我想知道是否可以在 Python 中表达一个类型,允许您为任意数量的 arguments 正确键入:

def test(*args):
  return args

At first glance, I came with:乍一看,我带着:

T = TypeVar('T')
def test(*args: T) -> Tuple[T, ...]:
  return args

But this of course will only type correctly the first T.但这当然只会正确输入第一个 T。

Is the only possible way to write the overrides for all arities?是为所有 arities 编写覆盖的唯一可能方法吗?

T1 = TypeVar('T1')
T2 = TypeVar('T2')
T3 = TypeVar('T3')
T4 = TypeVar('T4')

@overload
def test(arg1: T1) -> Tuple[T1]: ...
@overload
def test(arg1: T1, arg2: T2) -> Tuple[T1, T2]: ...
@overload
def test(arg1: T1, arg2: T2, arg3: T3) -> Tuple[T1, T2, T3]: ...
@overload
def test(arg1: T1, arg2: T2, arg3: T3, arg4: T4) -> Tuple[T1, T2, T3, T4]: ...
# etc
def test(*args: Any) -> Tuple[Any, ...]:
  return args

This is not complete either, since it does not carry enough type information to type something like:这也不完整,因为它没有携带足够的类型信息来键入以下内容:

x: Tuple[int, int, str] = test(*[1, 2, "4"])

TLDR: @overload is currently the only viable way to annotate some level of variance. TLDR: @overload目前是唯一可行的方法来注释某种程度的差异。 PEP 646 -- Variadic Generics is a draft proposal to enable proper annotation of variadics. PEP 646 -- 可变参数 Generics是一个提案草案,以启用可变参数的正确注释。


The correct approach to annotate *args is to decide on some level of supported "variance length", and explicitly type this using @overload .注释*args的正确方法是确定某种支持的“方差长度”级别,并使用@overload显式键入它。 Notably, explicit parameters must be positional only – they must be either __anonymous or positional, / .值得注意的是,显式参数只能是位置参数——它们必须是__anonymouspositional, / A final catch-all variadic @overload handles the case of more arguments.最后一个包罗万象的可变参数@overload处理更多 arguments 的情况。

from typing import TypeVar, Tuple, Any, overload

T = TypeVar('T')
T1 = TypeVar('T1')
T2 = TypeVar('T2')
T3 = TypeVar('T3')

# positional parameters via `, /` – Python 3.8+ only
@overload
def test(arg1: T1, /) -> Tuple[T1]: ...
# positional parameters via double underscore prefix
@overload
def test(__arg1: T1, __arg2: T2) -> Tuple[T1, T2]: ...
@overload
def test(__arg1: T1, __arg2: T2, __arg3: T3) -> Tuple[T1, T2, T3]: ...
# etc
...
# catch all variadic signature for all other cases
@overload
def test(*args: T) -> Tuple[T, ...]: ...

# implementation can use Any to simplify matching all overloads
def test(*args: Any) -> Tuple[Any, ...]:
  return args

reveal_type(test(1, 2, "three"))     # note: Revealed type is 'Tuple[builtins.int*, builtins.int*, builtins.str*]'
reveal_type(test(1, 2, "three", 4))  # note: Revealed type is 'builtins.tuple[builtins.object*]'
reveal_type(test(1, 2, 3, 4))        # note: Revealed type is 'builtins.tuple[builtins.int*]'

It is worth noting that while packing into variadic parameters can be typed, unpacking arguments generally cannot: Any container but tuple is arbitrary length – for example List[int] = [1, 2, 3] – and thus has no exact type information for its elements.值得注意的是,虽然打包成可变参数可以键入,但解包arguments 通常不能:除了tuple之外的任何容器都是任意长度的——例如List[int] = [1, 2, 3] ——因此没有确切的类型信息它的元素。

# unpack tuple of int, int
reveal_type(test(*(1, 2,)))  # note: Revealed type is 'Tuple[builtins.int*, builtins.int*]'
# unpack list of some ints
reveal_type(test(*[1, 2,]))  # note: Revealed type is 'builtins.tuple[builtins.int*]'

This can solved through TypeVarTuple from PEP646 , implemented in Python 3.11 or forward compat module typing-extensions .这可以通过TypeVarTuple中的TypeVarTuple解决,在 Python 3.11 或前向兼容模块类型扩展中实现。

Also see Dynamic TypeVar for a sequence of types for a solution.另请参阅Dynamic TypeVar 以了解解决方案的一系列类型

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM