引用泛型类型的类属性

Posted

技术标签:

【中文标题】引用泛型类型的类属性【英文标题】:Referring to class attributes of a generic type 【发布时间】:2019-10-27 04:24:59 【问题描述】:

我有一个抽象基类,它定义了几个本身就是类型的类属性(即嵌套类型);这些属性在从抽象类继承的具体派生类中被覆盖。具体派生类与其他几个被类型注释为泛型的类一起使用,其中类型参数具有必须从抽象类派生的类型约束。我希望这些其他类(泛型)引用具体类的类属性(嵌套类型)。下面的例子应该证明这个想法:

import typing

class Base:
    A: typing.Type[Base]

class One(Base):
    class A(Base):  # overrides Base.A
        pass

class Two(Base):
    class A(One):  # overrides Base.A
        pass


T = typing.TypeVar('T', bound=Base)

class Foo(typing.Generic[T]):                 # the generic
    def foo(self, a: typing.Type[T.A]) -> T:  # <-- here, see T.A
        ...

f: Foo[Two] = Foo()
f.foo(Two.A)        # line 22 is here

MyPy v0.701 在第 22 行报错:

“Foo”的“foo”的参数 1 具有不兼容的类型“Type[A]”;预期“类型[Two]”

MyPy 似乎忽略了T.A 的属性引用。我如何理解我试图通过属性.A 引用可用的类型?该属性保证可用,因为类型变量T 被限制为Base,它具有A。请注意,扩展泛型类型集不是一种选择;我可以详细说明这一点,但解释过于针对我的应用程序。

更新:带有--new-semantic-analyzer 的 MyPy v0.711 会产生更易于理解的错误消息:

名称“T.A”未定义

【问题讨论】:

请注意,您对 f.foo 的调用不正确,即使不是出于 mypy 所述的原因。你传入一个 Type[A] 的实例,但你的注解只需要一个 A 的实例。 foo 的主体实际上是...,还是您有可能将 T.A 限制为 T 的实现? 是的,确实,我搞砸了这个例子,同时剥离了与我的应用程序无关的细节,对不起。 foo 的主体比较复杂,这里就不展示了;它需要在派生类中定义的 A 的特定实现。 【参考方案1】:

我不确定这是否有帮助,但如果你参数化绑定到 Base 的参数,它会起作用:

class Base:
    A: typing.Type['Base']


class One(Base):
    class A(Base):  # overrides Base.A
        pass


class Two(Base):
    class A(One):  # overrides Base.A
        pass


T = typing.TypeVar('T', bound='Base')
A = typing.TypeVar('A', bound='Base')


class Foo(typing.Generic[T, A]):    # the generic
    def foo(self, a: typing.Type[A]) -> T:  # <-- here, see T.A
        pass


f: Foo[Two, Two.A] = Foo()
f.foo(Two.A)        # line 22 is here

【讨论】:

谢谢。不幸的是,正如我在问题的最后提到的那样,添加一个新的类型参数是非常不可取的,因为它会使面向用户的 API 变得不必要地复杂。到那时,我预计用户可能会想只用# type: ignore 粘贴代码,而不是处理类型参数。或许我应该把我的代码全部发布在这里进行深入审查? 我很难过。我认为这可能与协方差有关,但将该属性添加到 TypeVar 似乎没有帮助。最奇怪的是错误消息 expected "Type[Two]" 就像它只是忽略了 Type 参数的 .A 部分。这感觉就像 MyPy 中的词法分析器错误。

以上是关于引用泛型类型的类属性的主要内容,如果未能解决你的问题,请参考以下文章

C#实体类中如何定义泛型集合类型的属性?

Java泛型

引用泛型类型的类属性

泛型类中的数组

对泛型的理解

WCF服务引用之后自动生成的泛型代理类名称太长的解决方案