代码之家  ›  专栏  ›  技术社区  ›  exhuma

如何正确地向Mixin类添加类型提示?

  •  5
  • exhuma  · 技术社区  · 6 年前

    考虑下面的例子。该示例是精心设计的,但在可运行的示例中说明了这一点:

    class MultiplicatorMixin:
    
        def multiply(self, m: int) -> int:
            return self.value * m
    
    
    class AdditionMixin:
    
        def add(self, b: int) -> int:
            return self.value + b
    
    
    class MyClass(MultiplicatorMixin, AdditionMixin):
    
        def __init__(self, value: int) -> None:
            self.value = value
    
    
    instance = MyClass(10)
    print(instance.add(2))
    print(instance.multiply(2))
    

    12
    20
    

    代码有效。

    但是跑步 mypy 在它上,产生以下错误:

    example.py:4: error: "MultiplicatorMixin" has no attribute "value"
    example.py:10: error: "AdditionMixin" has no attribute "value"
    

    我明白为什么mypy会给出这个结果。但是mixin类从来没有自己使用过。它们总是用作额外的超类。

    对于context,这是一个已经在现有应用程序中使用的模式,我正在添加类型提示。在这种情况下,错误是误报。我正在考虑使用mixin重写这个部分,因为我不太喜欢它,同样也可以通过重新组织类层次结构来完成。

    但我还是想知道这样的事情是如何被恰当地暗示出来的。

    2 回复  |  直到 6 年前
        1
  •  4
  •   Sraw    6 年前

    我已经在我的机器上测试过了,希望它也能为你工作:

    class MultiplicatorMixin:
        value = None # type: int
    
        def multiply(self, m: int) -> int:
            return self.value * m
    
    
    class AdditionMixin:
        value = None # type: int
    
        def add(self, b: int) -> int:
            return self.value + b
    
    
    class MyClass(MultiplicatorMixin, AdditionMixin):
    
        def __init__(self, value: int) -> None:
            self.value = value
    
    
    instance = MyClass(10)
    print(instance.add(2))
    print(instance.multiply(2))
    
        2
  •  6
  •   soerface    6 年前

    我看到的一种方法 this question 类型是否暗示 self 属性。一起 Union 在输入包中,您可以使用与mixin一起使用的类中的属性,同时仍然对自己的属性具有正确的类型提示:

    from typing import Union
    
    class AdditionMixin:
    
        def add(self: Union[MyBaseClass, 'AdditionMixin'], b: int) -> int:
            return self.value + b
    
    
    class MyBaseClass:
    
        def __init__(self, value: int):
            self.value = value
    

        3
  •  4
  •   Campi    5 年前

    作为参考,mypy建议通过协议实现mixin( https://mypy.readthedocs.io/en/latest/more_types.html#advanced-uses-of-self-types

    它适用于mypy>=750。

    from typing_extensions import Protocol
    
    
    class HasValueProtocol(Protocol):
        @property
        def value(self) -> int: ...
    
    
    class MultiplicationMixin:
    
        def multiply(self: HasValueProtocol, m: int) -> int:
            return self.value * m
    
    
    class AdditionMixin:
    
        def add(self: HasValueProtocol, b: int) -> int:
            return self.value + b
    
    
    class MyClass(MultiplicationMixin, AdditionMixin):
    
        def __init__(self, value: int) -> None:
            self.value = value
    
        4
  •  0
  •   Artur Barseghyan    5 年前

    除了上面提到的好答案。我的用例-要在测试中使用的mixins。

    正如吉多·范·罗森自己提出的那样 here :

    from typing import *
    T = TypeVar('T')
    
    class Base:
        fit: Callable
    
    class Foo(Base):
        def fit(self, arg1: int) -> Optional[str]:
            pass
    
    class Bar(Foo):
        def fit(self, arg1: float) -> str:
            pass    
    

    因此,当涉及到mixin时,它可以如下所示:

    
    class UsefulMixin:
    
        assertLess: Callable
        assertIn: Callable
        assertIsNotNone: Callable
    
        def something_useful(self, key, value):
            self.assertIsNotNone(key)
            self.assertLess(key, 10)
            self.assertIn(value, ['Alice', 'in', 'Wonderland']
    
    
    class AnotherUsefulMixin:
    
        assertTrue: Callable
        assertFalse: Callable
        assertIsNone: Callable
    
        def something_else_useful(self, val, foo, bar):
            self.assertTrue(val)
            self.assertFalse(foo)
            self.assertIsNone(bar)  
    

    class TestSomething(unittest.TestCase, UsefulMixin, AnotherUsefulMixin):
    
        def test_something(self):
            self.something_useful(10, 'Alice')
            self.something_else_useful(True, False, None)