如何正确向复杂的 Mixin 类添加类型提示?

问题描述 投票:0回答:1

向 mixin 类添加类型注释以使代码通过 mypy 检查的正确方法是什么:

example1.py:

class HostClass:
    def host_method(self) -> None:
        pass

    def overridden_host_method(self) -> None:
        pass

class MixinClass:
    def mixin_method(self) -> None:
        self._other_mixin_method()
        self.host_method()

    def _other_mixin_method(self) -> None:
        pass

    def overridden_host_method(self) -> None:
        super().overridden_host_method()


class MyClass(MixinClass, HostClass):
    pass


obj = MyClass()
obj.mixin_method()
obj.overridden_host_method()
% mypy example1.py
example1.py:11: error: "MixinClass" has no attribute "host_method"  [attr-defined]
example1.py:17: error: "overridden_host_method" undefined in superclass  [misc]
Found 2 errors in 1 file (checked 1 source file)

mypy 文档中推荐的解决方案(https://mypy.readthedocs.io/en/latest/more_types.html#mixin-classes)以及如何正确地将类型提示添加到 Mixin 类的答案中? 不起作用。

example2.py(HostProtocol 作为“self”的类型注释):

from typing import Protocol


class HostProtocol(Protocol):
    def host_method(self) -> None:
        ...

    def overridden_host_method(self) -> None:
        ...


class HostClass:
    def host_method(self) -> None:
        pass

    def overridden_host_method(self) -> None:
        pass


class MixinClass:
    def mixin_method(self: HostProtocol) -> None:
        self._other_mixin_method()
        self.host_method()

    def _other_mixin_method(self) -> None:
        pass

    def overridden_host_method(self) -> None:
        super().overridden_host_method()


class MyClass(MixinClass, HostClass):
    pass


obj = MyClass()
obj.mixin_method()
obj.overridden_host_method()
% mypy example2.py
example2.py:22: error: "HostProtocol" has no attribute "_other_mixin_method"  [attr-defined]
example2.py:29: error: "overridden_host_method" undefined in superclass  [misc]
Found 2 errors in 1 file (checked 1 source file)

example3.py(HostProtocol 作为 MixinClass 的基类):

from typing import Protocol

class HostProtocol(Protocol):
    def host_method(self) -> None:
        ...
 
    def overridden_host_method(self) -> None:
        ...


class HostClass:
    def host_method(self) -> None:
        pass

    def overridden_host_method(self) -> None:
        pass

class MixinClass(HostProtocol):
    def mixin_method(self) -> None:
        self._other_mixin_method()
        self.host_method()

    def _other_mixin_method(self) -> None:
        pass

    def overridden_host_method(self) -> None:
        super().overridden_host_method()


class MyClass(MixinClass, HostClass):
    pass


obj = MyClass()
obj.mixin_method()
obj.overridden_host_method()
% mypy example3.py
example3.py:34: error: Cannot instantiate abstract class "MyClass" with abstract attribute "host_method"  [abstract]
example3.py:34: note: "host_method" is implicitly abstract because it has an empty function body. If it is not meant to be abstract, explicitly `return` or `return None`.
Found 1 error in 1 file (checked 1 source file)

example4.py(切换MyClass基类的顺序):

from typing import Protocol

class HostProtocol(Protocol):
    def host_method(self) -> None:
        ...
 
    def overridden_host_method(self) -> None:
        ...


class HostClass:
    def host_method(self) -> None:
        pass

    def overridden_host_method(self) -> None:
        pass

class MixinClass(HostProtocol):
    def mixin_method(self) -> None:
        self._other_mixin_method()
        self.host_method()

    def _other_mixin_method(self) -> None:
        pass

    def overridden_host_method(self) -> None:
        print("Mixin overrides")
        super().overridden_host_method()


class MyClass(HostClass, MixinClass):
    pass


obj = MyClass()
obj.mixin_method()
obj.overridden_host_method()
mypy example4.py
Success: no issues found in 1 source file

但是 overridden_host_method() 不再被重写:

% python example4.py
% 

我。 e.不打印“Mixin overrides”。

MixinClass 应:

  • 从 HostClass 调用方法
  • 也从自身调用一个方法
  • 重写 HostClass 的方法
python python-typing mypy mixins
1个回答
1
投票

使用

typing.Protocol
的建议是在最小化运行时和静态类型之间差异的基础上给出的。然而,在大多数情况下,我会避免使用
typing.Protocol
,包括 mixin 类。它们不容易使用,正如您在
example2.py
(如果没有
交叉类型
,注释 self 很尴尬)和
example3.py
(基类顺序很难正确,如果可能的话)中发现的那样;这还引入了一个隐藏的元类)。

如果您广泛依赖静态类型并且不介意运行时类型和静态类型之间的差异,那么对于 mixin 类有更好的解决方案。

from __future__ import annotations

import typing_extensions as t

if t.TYPE_CHECKING:
    MixinBase: t.TypeAlias = "HostClass"
else:
    MixinBase = object

class HostClass:
    def host_method(self) -> None: pass
    def overridden_host_method(self) -> None: pass

class MixinClass(MixinBase):
    def mixin_method(self) -> None:
        self._other_mixin_method()
        self.host_method()                       # No Errors

    def _other_mixin_method(self) -> None: pass

    def overridden_host_method(self) -> None:
        super().overridden_host_method()         # No Errors

class MyClass(MixinClass, HostClass): pass
>>> obj = MyClass()
>>> obj.mixin_method()
>>> obj.overridden_host_method()
© www.soinside.com 2019 - 2024. All rights reserved.