如何为装饰器指定泛型类型的别名

Zul*_*lan 11 python mypy python-typing

考虑绑定到某些类的类型化装饰器的示例

import unittest
from typing import *

T = TypeVar("T", bound=unittest.TestCase)

def decorate(func: Callable[[T], None]) -> Callable[[T], None]:
    def decorated_function(self: T) -> None:
        return func(self)
    return decorated_function
Run Code Online (Sandbox Code Playgroud)

现在我什至有一个生成器来创建这些装饰器并想要速记这些装饰器。我对存储装饰器的变量使用什么类型(省略生成器的简化示例)。

my_decorate: Callable[[Callable[[T], None]], Callable[[T], None]] = decorate
Run Code Online (Sandbox Code Playgroud)

这可行,但很笨拙。所以问题是:

如何为该类型别名以避免编写完整签名?


不起作用的事情:

TD = Callable[[Callable[[T], None]], Callable[[T], None]]
my_decorate: TD[T] = decorator_variable
Run Code Online (Sandbox Code Playgroud)

给出错误

error: Type variable "mypytest.T" is unbound
note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class)
note: (Hint: Use "T" in function signature to bind "T" inside a function)
Run Code Online (Sandbox Code Playgroud)

相反,我可以用作TD[T]函数的参数类型。

仅使用my_decorate: TD = ...会产生--strict错误

error: Missing type parameters for generic type "TD"
Run Code Online (Sandbox Code Playgroud)

并且它不再检测错误的my_decorate.

Mis*_*agi 3

在许多情况下,当Callable太有限时,请使用 aProtocol代替:

class TD(Protocol):
    """Type of any callable `(T -> None) -> (T -> None)` for all `T`"""
    def __call__(self, __original: Callable[[T], None]) -> Callable[[T], None]:
        ...
Run Code Online (Sandbox Code Playgroud)

TD不是泛型类型,因此不需要“填充”类型变量可以直接作为注解使用:

my_decorate: TD = decorate
Run Code Online (Sandbox Code Playgroud)

值得注意的是,TD.__call__尽管不是通用的,但仍然TD是通用的可调用对象。它T根据需要由每个调用的上下文填充。