问题
假设我想实现一个类装饰器,为现有的类添加一些属性和函数。
特别地,假设我有一个名为HasNumber
的协议,并且我需要一个decoratorcan_add
,它添加缺失的方法来将HasNumber
类转换为CanAdd
。
class HasNumber(Protocol):
num: int
class CanAdd(HasNumber):
def add(self, num: int) -> int: ...
实施
我实现装饰器如下:
_HasNumberT = TypeVar("_HasNumberT", bound=HasNumber)
def can_add(cls: Type[_HasNumberT]) -> Type[CanAdd]:
def add(self: _HasNumberT, num: int) -> int:
return self.num + num
setattr(cls, "add", add)
return cast(Type[CanAdd], cls)
@can_add
class Foo:
num: int = 12
错误
当我运行代码时,它运行得很好,但由于某种原因,mypy对此感到不高兴。
它给出了错误"Foo" has no attribute "add" [attr-defined]
,就好像它没有考虑can_add
装饰器的返回值(注释为Type[CanAdd]
)一样。
foo = Foo()
print(foo.add(4)) # "Foo" has no attribute "add" [attr-defined]
reveal_type(foo) # note: Revealed type is "test.Foo"
问题
在本期中,有人演示了一种用Intersection
对此进行注释的方法。然而,有没有一种方法可以在没有Intersection
的情况下实现它?(假设除了协议中定义的属性之外,我不关心Foo
中的其他属性)
或者,这是八哥自身的局限吗?
没有解决我的问题的相关帖子:
类装饰器上的Mypy注释cast
告诉mypy
cls
(具有或不具有add
属性)可以安全地用作can_add
的返回值。它不能保证协议有效。
因此,mypy
不能判断Foo
是否被赋予了add
属性,只能判断使用can_add
装饰器是可以的。can_add
具有定义add
属性的副作用这一事实对mypy
来说是不可见的。
然而,您可以用直接继承来替换装饰器,比如
class HasNumber(Protocol):
num: int
_HasNumberT = TypeVar("_HasNumberT", bound=HasNumber)
class Adder(HasNumber):
def add(self, num: int) -> int:
return self.num + num
class Foo(Adder):
num: int = 12
foo = Foo()
print(foo.add(4))