kwargs 的通用类型提示

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

我正在尝试将 blinker 的信号类包装为强制输入的信号类。我试图包装的基本界面是:

class Signal:
  def send(self, sender: Any | None, **kwargs):
    ...

  def connect(self, receiver):
    ...

(这不是确切的界面,但模拟了问题)。

我尝试了几种方法:

使用 TypeVar 和 TypeVarTuple

我可以使用泛型非常简单地实现

sender
arg 的类型提示:

T = TypeVar("T")

class TypedSignal(Generic[T], Signal):
    def send(self, sender: Type[T], **kwargs):
        super(TypedSignal, self).send(sender)

    def connect(self, receiver: Callable[[Type[T], ...], None]) -> Callable:
        return super(TypedSignal, self).connect(receiver)

# used as
my_signal = TypedSignal[MyClass]()

棘手的是当我想为

kwargs
添加类型检查时。我一直在尝试使用的方法是使用可变参数泛型和
Unpack
,如下所示:

T = TypeVar("T")
KW = TypeVarTuple("KW")

class TypedSignal(Generic[T, Unpack[KW]], Signal):
    def send(self, sender: Type[T], **kwargs: Unpack[Type[KW]]):
        super(TypedSignal, self).send(sender)

    def connect(self, receiver: Callable[[Type[T], Unpack[Type[KW]]], None]) -> Callable:
        return super(TypedSignal, self).connect(receiver)

但是 mypy 抱怨:

error: Unpack item in ** argument must be a TypedDict
这看起来很奇怪,因为即使不使用泛型也会抛出这个错误,更不用说当
TypedDict
被传递时。

使用 ParamSpec 和协议

P = ParamSpec("P")

class TypedSignal(Generic[P], Signal):
    def send(self, *args: P.args, **kwargs: P.kwargs) -> None:
        super().send(*args, **kwargs)

    def connect(self, receiver: Callable[P, None]):
        return super().connect(receiver=receiver)


class Receiver(Protocol):
    def __call__(self, sender: MyClass) -> None:
        pass

update = TypedSignal[Receiver]()


@update.connect
def my_func(sender: MyClass) -> None:
    pass

update.send(MyClass())

但是 mypy 似乎包装了协议,因此它需要一个采用协议的函数,从而出现以下错误:

 error: Argument 1 to "connect" of "TypedSignal" has incompatible type "Callable[[MyClass], None]"; expected "Callable[[Receiver], None]"  [arg-type]
 error: Argument 1 to "send" of "TypedSignal" has incompatible type "MyClass"; expected "Receiver"  [arg-type]

总结

有更简单的方法吗?这对于当前的 python 打字来说可能吗?

mypy 版本是 1.9.0 - 尝试使用早期版本,但它完全崩溃了。

python generics mypy python-typing
1个回答
2
投票

我认为这是不可能的。

您可以通过 3 种方式注释

**kwargs

  1. **kwargs: Foo
    ->
    kwargs
    的类型为
    dict[str, Foo]
  2. **kwargs: Unpack[TD]
    ,其中
    TD
    TypedDict
    ->
    kwargs
    的类型为
    TD
  3. *args: P.args, **kwargs: P.kwargs
    ,其中
    P
    ParamSpec
    ->
    kwargs
    是通用的
    P

因此,拥有泛型

kwargs
的唯一方法是将 1. 与常规
TypeVar
一起使用,这意味着所有关键字参数都需要具有兼容的类型,或者使用 3. 但目前 要求您还拥有
*args 
,你没有。

© www.soinside.com 2019 - 2024. All rights reserved.