如果 mixin 的目标类继承自元类,如何键入提示?

问题描述

考虑下面的类和 mixin:

class Target(ClassthatUsesAMetaclass):

    def foo(self):
        pass

class Mixin:

    def __init__(self):
        self.foo()  # type error: type checker doesn't kNow Mixin will have
                    # access to foo once in use.

class Combined(Mixin,Target):

    def __init__(self):
        Target.__init__(self)
        Mixin.__init__(self)

我试图避免上述场景中的类型检查器错误。一种选择是这样的:

from typing import Protocol

class Fooable(Protocol):
    def foo(self): ...

class Mixin(Fooable):

    def __init__(self):
        self.foo()

效果很好,除了 Target 继承自使用元类的类,因此 Combined 不能同时继承 TargetMixin。 所以现在我正在尝试另一种方法,在 self 中注释 Mixin

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from .this import Mixin,Target
    Mixin_T = type('Mixin_T',(Mixin,Target),{})


class Mixin:

    def __init__(self: Mixin_T):
        self.foo()  # No longer an error

class Combined(Mixin,Target):

    def __init__(self):
        Target.__init__(self)
        Mixin.__init__(self)  # Now this is an error: "Type[Mixin]" is not
                              # assignable to parameter "self"
                              # "Mixin" is incompatible with "Mixin_T"

那么除了使用 # type: ignore 之外,我应该如何赢得这场比赛?

解决方法

除此之外,除了提供一些说明之外,上面几乎没有代码和一些误解使这个问题根本无法回答。

首先,您确定要“从元类继承”吗?除非创建另一个元类,否则继承元类是没有意义的。您的片段显示您从假设的元类(未给出代码)继承,以创建 Target 并且它们尝试使用 Target 作为普通类(非元类)的父类。这毫无意义。

您可能只是混淆了术语,隐藏的 InheritFromMetaclass 类实际上只是使用元类,而不是“继承”它。那么您的问题根本与元类无关。

因此,代码段中真正可见的问题是静态检查器没有“看到” Mixin 类中的 self.foo 方法 - 你猜怎么着? self.foo没有 Mixin 方法 - 检查器只是在你面前抛出一个冷酷的事实:而 Python 确实允许引用方法和在类中不可用的属性,知道它将与具有这些属性的其他类一起使用,这不是好的设计并且容易出错。那种糟糕的设计静态类型检查存在于淘汰中。

因此,您需要的是拥有 Mixin 的基础,它是一个抽象类,并将 Foo 作为抽象方法。 (或者让 Mixin 本身就是那个抽象类)。

如果 - 由于使用了其他元类,由于元类冲突,您不能从 abc.ABC 中继承 Mixin,您必须:从 InheritsFromMetaclass 实际使用的元类创建一个组合元类ABCMeta ,然后将其用作 Mixin 的元类 - 或者只是按原样在 foo 中创建一个存根 Mixin 方法(这可能会引发 NotImplementedError - 因此具有与抽象方法相同的行为,但不必真正继承它。

重要的部分是,您在类体内的代码中访问的方法和属性必须存在于该类中,而不依赖于将存在于它的子类中的属性。

如果这不能解决您的问题,您需要提供更多数据 - 包括涉及您的实际元类的可重现的完整示例。 (并且可以通过组合上述元类来解决)

,

我找到了一个非常简单的解决方案:

if TYPE_CHECKING:
    from .this import Target
    Mixin_T = Target
else:
    Mixin_T = object

class Mixin(Mixin_T):
    ...

现在所有 Target 的方法都可以在 Mixin 内被类型检查器识别,并且不需要将 self 的类型覆盖为与 Mixin 不兼容的类型.如果 mixin 是针对所有类型的 Target 类,这可能有点尴尬,但对于我的使用来说,这是完全可以接受的,因为我的情况是一组 mixin 扩展了一个非常特定的目标类。