如何将 TypeVarTuple 转换为 ParamSpec?

问题描述 投票:0回答:1

(我几乎可以肯定问题的措辞没有意义,但我还没有找到更好的措辞。)

我有以下代码需要进行类型检查:

from collections.abc import Callable
from typing import Generic, TypeVar, TypeVarTuple

C = TypeVar("C")
T = TypeVar("T")
Ts = TypeVarTuple("Ts")

class Decorator1(Generic[T]):
    def __init__(self, /, type: type[T]) -> None:
        super().__init__()

    def __call__(self, function: Callable[[C, T], None], /) -> Callable[[C, T], None]:
        return function

class DecoratorNa(Generic[T]):
    # Q: how to prevent "T" from becoming "object" here?
    def __init__(self, /, *type: type[T]) -> None:
        super().__init__()

    def __call__(self, function: Callable[[C, T], None], /) -> Callable[[C, T], None]:
        return function

class DecoratorNb(Generic[*Ts]):
    def __init__(self, /, *types: *Ts) -> None:
        super().__init__()

    # Q: how to convert "*Ts" from "type[int], type[str]" to "int, str" here?
    def __call__(
        self, function: Callable[[C, *Ts], None], /
    ) -> Callable[[C, *Ts], None]:
        return function

class MyClass:
    # This is given and supposed to type-check:
    @Decorator1(int)
    def print_number(self, number: int) -> None: ...

    @Decorator1(str)
    def print_string(self, string: str) -> None: ...

    # # This is supposed to error:
    # @Decorator1(int)
    # def print_another_string(self, string: str) -> None: ...

    # This is supposed to type-check:
    @DecoratorNa(int, str)
    def print_number_and_string_a(self, number: int, string: str) -> None: ...

    # This is supposed to type-check:
    @DecoratorNb(int, str)
    def print_number_and_string_b(self, number: int, string: str) -> None: ...

Decorator
的构造函数在运行时接受一种(或多种)类型(比较
PySide6.QtCore.Slot()
),并且修饰函数应该接受该类型的一个(或多个)参数。

这对于使用

type[T]

 的单一类型效果很好,但我无法为多种类型找到相同的东西。我尝试过 
*type: type[T]
 但评估结果为 
T = object
。对于 
TypeVarTuple
,我最终不知道如何将 
*Ts
 转换为 
ParamSpec
 之类的东西。我知道 
typing.get_args
,但我不确定在类型检查时应该如何应用它。

python python-typing python-decorators
1个回答
0
投票
正如评论中提到的,您使用的确切语法当前不起作用。类型表单允许类型检查器理解接受类型 (

DecoratorNa(int, str)

) 的调用表达式,目前正在提交,并且处于修订的最后阶段
1,但截至今天,还没有任何具体的 TypeVarTuple
 转变。

但是,如果您愿意将语法从

__call__

 (
DecoratorNa(int, str)
) 切换为 
__getitem__
 (
DecoratorN[int, str]
) 表达式,以下内容可能适合您(
mypy PlaygroundPyright Playground) :

... from types import MethodType ... class DecoratorN(Generic[*Ts]): def __init__( self, function: Callable[[Any, *Ts], None], / ) -> None: super().__init__() self._function = function def __get__(self, instance: Any, owner: type[Any], /) -> Callable[[*Ts], None]: return MethodType(self._function, instance) # type: ignore[return-value] class MyClass: # This is given and supposed to type-check: @Decorator1[int] def print_number(self, number: int) -> None: ... @Decorator1[str] def print_string(self, string: str) -> None: ... @Decorator1[int] # error def print_another_string(self, string: str) -> None: ... # This is supposed to type-check: @DecoratorN[int, str] def print_number_and_string(self, number: int, string: str) -> None: ...
mc: MyClass = MyClass()
mc.print_number(1)
mc.print_number("")  # error
mc.print_string("")
mc.print_string(1)  # error
mc.print_number_and_string(1, "")
mc.print_number_and_string("", 1)  # error
这里,方括号允许正确解释为 

DecoratorN[*Ts]

 类型的类型参数化,然后用于强制修饰函数具有相同的参数类型 (
*Ts
)。

如果您不喜欢拥有

DecoratorN

 实例的想法,并且需要保留原始实例方法类型(如问题中的示例所示),您可以执行以下操作(不完整的实现):

if typing.TYPE_CHECKING: class DecoratorN(Generic[*Ts]): ... else: class _DecoratorNType: def __getitem__(self, /, *types_): return type(self)(*types_) def __call__(self, function): return function DecoratorN = _DecoratorNType()


    Pyright 已经有了实验性支持(请参阅
  1. https://github.com/microsoft/pyright/pull/8875
© www.soinside.com 2019 - 2024. All rights reserved.