带有有界参数的类型提示装饰器

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

我想做的一个例子如下:

@dataclass
class Arg1:
    x = field()


@dataclass
class Arg2:
    x = field()


@dataclass
class Obj:
    y = field()

    T = TypeVar("T")
    R = TypeVar("R", bound=(Arg1 | Arg2))
    C = TypeVar("C", bound=Callable[[Self, R], T]) 

    @staticmethod
    def deco(f: C) -> C:
        def wrapper(self: Self, arg: Obj.R) -> Obj.T:
            print(arg.x)
            print(self.y)   
            return f(self, arg)

        return wrapper

    @deco
    def f1(self: Self, arg: Arg1) -> str:
        return "ok"  

    @deco
    def f2(self: Self, arg: Arg2) -> int:
        return 3

我从 mypy 得到以下两个错误:

test.py:22: error: A function returning TypeVar should receive at least one argument containing the same TypeVar  [type-var]
test.py:26: error: Incompatible return value type (got "Callable[[Self, Obj.R], Obj.T]", expected "C")  [return-value]

如果我删除

T
并将它的使用替换为
Any
,我可以修复第一个错误(以削弱我的类型断言为代价)但仍然会出现第二个错误。这一点让我特别困惑,因为 mypy 似乎在抱怨我没有返回
C
时,还给我了
C
的定义。我应该怎么做才能说服它这里的类型等价?

关于第一个错误,我希望我可以通过在

C
中解压缩
def deco(f: C) -> C
的定义来解决它,但是我将失去传递给
f
的参数类型与类型相同的限制
deco
的返回值的参数。我能做些什么吗?

编辑:在评论中讨论后,我将 deco 的定义更新为

def deco(f: Callable[[Self, R], T]) -> Callable[[Self, R], T]:
现在得到以下错误:

test.py:21: error: Static methods cannot use Self type  [misc]

这似乎是进步,但我也不确定该怎么做。

python python-decorators mypy python-typing python-3.11
2个回答
1
投票

我会把装饰器移到课外。它并不是真正特定于您的班级,因为您需要的所有类型信息都可以用其他类型变量指定。

这似乎就足够了(至少我的杂种设置使用 Python 3.9 和

mypy
1.0.1;
Self
typing_extensions
提供,而不是标准库
typing
模块)。

T = TypeVar("T")
S = TypeVar("S", bound='Obj')
R = TypeVar("R", Arg1, Arg2)  # constrained variable, not a bound variable

def deco(f: Callable[[S, R], T]) -> Callable[[S, R], T]:
    def wrapper(self: S, arg: R) -> T:
        print(arg.x)
        print(self.y)
        return f(self, arg)
    return wrapper

class Obj:
    @deco
    def f1(self: Self, arg: Arg1) -> str:
        return "ok"

    @deco
    def f2(self: Self, arg: Arg2) -> int:
        return 3

我们将

R
设为约束变量,这样我们就可以知道
arg
是函数主体中
Arg1
Arg2
的实例,而不是这两种类型之一的未知实例。


1
投票

这是在 mypy 1.1.1 中进行类型检查的东西,我希望它是你需要的:

from dataclasses import dataclass, field
from typing import TypeVar, Callable

@dataclass
class Arg1:
    x = field()


@dataclass
class Arg2:
    x = field()

T = TypeVar("T")
R = TypeVar("R", Arg1, Arg2)
ObjSelf = TypeVar('ObjSelf', bound='Obj')

def deco(f: Callable[[ObjSelf, R], T]) -> Callable[[ObjSelf, R], T]:
    def wrapper(self: ObjSelf, arg: R) -> T:
        print(arg.x)
        print(self.y)
        return f(self, arg)

    return wrapper


@dataclass
class Obj:
    y = field()

    @deco
    def f1(self, arg: Arg1) -> str:
        return "ok"  

    @deco
    def f2(self, arg: Arg2) -> int:
        return 3

解释一下:

Self
有点魔力,不能很好地处理你正在做的那种非平凡的价值杂耍,所以我又回到了过去糟糕的日子里我们必须做的事情:一个类型具有明确界限的变量。

C
不得不去,因为不是每个
R
都一样,就像不是每个
T
都一样,所以我们需要在
deco
的类型签名中明确列出它们,所以 mypy 知道什么类型变量的作用域。我将类型变量和
deco
拉到
Obj
之外,因为它的复杂性让我很头疼。


Edit:chepner 是对的,在这种情况下,

R
的约束类型变量优于绑定类型。

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