【问题标题】:How to alias generic types for decorators如何为装饰器命名泛型类型
【发布时间】:2021-12-21 14:25:52
【问题描述】:

考虑example of a typed decorator bound to certain classes

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

现在我什至有a generator that creates these decorators 并想简写这些装饰器。我对存储装饰器的变量变量是什么类型(省略生成器的简化示例)。

my_decorate: Callable[[Callable[[T], None]], Callable[[T], None]] = decorate

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

我如何为这种类型设置别名以避免编写完整的签名?


不起作用的事情:

TD = Callable[[Callable[[T], None]], Callable[[T], None]]
my_decorate: TD[T] = decorator_variable

给出错误

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)

相比之下,我可以使用TD[T] 作为函数的参数类型。

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

error: Missing type parameters for generic type "TD"

它不再检测my_decorate的错误应用程序。

【问题讨论】:

  • my_decorate: TD = decorator_variable 不起作用(没有将T 设置为Any)?
  • 省略[T] 会产生--strict 错误,并且不再对self 执行预期的类型检查。我在问题中添加了详细信息。
  • 它们(类型变量)用作泛型类型以及泛型函数定义的参数。 但是您尝试在上下文之外使用类型变量。
  • @alex_noname 你是对的。我如何在没有合理的类型别名的情况下使用?
  • 您可能想查看有关键入装饰器的 Python 文档:docs.python.org/3/library/typing.html#functions-and-decorators 另外,这是 Python。请不要被整个打字过程冲昏了头脑。

标签: python mypy python-typing


【解决方案1】:

这个呢?它比完整签名短:

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


decorator_variable: Callable[[Callable[[T], None]], Callable[[T], None]] = decorate

U = Callable[[T], None]
my_decorate: Callable[[U[T]], U[T]] = decorator_variable

【讨论】:

    【解决方案2】:

    在许多情况下,当Callable 过于有限时,请改用Protocol

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

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

    my_decorate: TD = decorate
    

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

    【讨论】:

    • 我不知道Protocol。我今天学了些新东西。感谢您的回答@MisterMiyagi。
    • 绝妙的组合...我知道Protocol,但是将它与__call__ 一起使用以仅将其应用于函数就可以了!完美运行。
    猜你喜欢
    • 1970-01-01
    • 2018-01-07
    • 1970-01-01
    • 2016-12-14
    • 2012-11-12
    • 2011-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多