如果mixin的目标类继承自元类,如何提示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。所以现在我正在尝试另一种选择,在Mixin中注释self:

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,我该如何赢得这场比赛呢?

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

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扩展一个非常特定的目标类。

除此之外,除了提供一些澄清外,上面有很少的代码和一些误解使得这个问题根本无法回答。

首先,你确定你是从一个元类继承的吗??除非创建另一个元类,否则继承元类没有意义。您的代码片段显示您从一个假定的元类(没有给出代码)中写入,以创建Target,并试图使用Target作为正常类(非元类)的父类。这没有意义。

您可能混淆了术语,隐藏的InheritFromMetaclass类实际上只是使用元类,而不是"继承";从它。那么你的问题就与元类无关了。

所以,代码片段中真正可见的问题是静态检查器没有"看到"一个self.foo方法在Mixin类-你猜怎么着?在Mixin中没有self.foo方法——检查器只是在你面前扔了一个冷酷的事实:虽然Python确实允许一个人引用类中不可用的方法和属性,知道它将与其他具有这些属性的类一起使用,这不是一个好的设计,而且容易出错。不良设计静态类型检查的存在是为了清除。

所以,你需要的是有一个Mixin的基础,它是一个抽象类,并有Foo作为一个抽象方法。(或者让Mixin本身成为那个抽象类)。

如果-由于使用其他元类,由于元类冲突,您无法从abc.ABC中获得Mixin inheit,您必须要么:从InheritsFromMetaclassABCMeta实际使用的元类中创建一个组合元类,并将其用作Mixin的元类-或者只是在Mixin中创建一个stubfoo方法(可以引发NotImplementedError-因此具有与抽象方法相同的行为,但实际上不必继承它。

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

如果这不能解决您的问题,则需要提供更多的数据—包括一个涉及实际元类的可重复的完整示例。(如果仅仅通过如上所述组合元类就可以解决这个问题)

最新更新