【发布时间】:2021-10-22 17:54:41
【问题描述】:
问题
假设我想实现一个类装饰器,为现有类添加一些属性和功能。
特别是,假设我有一个名为HasNumber 的协议,我需要一个装饰器can_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"
问题
在这个issue 中,有人演示了一种用Intersection 注释它的方法。但是,有没有办法在没有Intersection 的情况下实现它? (假设我不关心Foo中的其他属性,除了协议中定义的属性)
或者,是不是mypy本身的限制?
没有解决我的问题的相关帖子:
【问题讨论】:
-
我怀疑这是
mypy的缺点。cast抑制了对返回的东西不是CanAdd的任何担忧,但如果add实际上已添加到cls参数中,则没有静态保证。 -
因此,
mypy无法确认Foo具有add属性,只是您说Foo(带有或不带有add属性)可以是can_add的返回值。 -
我对 mypy 的期望是它用
can_add的返回类型替换装饰类,即Type[CanAdd],而不关心实际实现的实际情况(因为我故意忽略它与cast)。