Mypy 无法识别类装饰器



问题

假设我想实现一个类装饰器,为现有的类添加一些属性和函数。

特别地,假设我有一个名为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注释
  • 适用于Mypy的Class Decorator
  • cast告诉mypycls(具有或不具有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))
    

    相关内容

    • 没有找到相关文章

    最新更新