我想做的一个例子如下:
@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 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
的实例,而不是这两种类型之一的未知实例。
这是在 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
的约束类型变量优于绑定类型。