【问题标题】:Mypy Unsupported type Type[typeVarAlias]Mypy 不支持的类型 Type[typeVarAlias]
【发布时间】:2019-11-20 07:26:14
【问题描述】:

Mypy 返回一个我不理解的错误(我也不能以简化的形式重新创建)。谷歌搜索错误被证明是困难的(给定问号)。

有谁知道这个错误是什么意思?问号具体代表什么?

Unsupported type Type[typeBasePage?]

有问题的代码:

typeBasePage = TypeVar("typeBasePage", bound="BasePage")  # any instance subclass of BasePage
typeNavCls = Type[typeBasePage] # error occurs here - trying to make an alias

class Nav:
    def __init__(self, cls: typeNavCls):
        self.cls: typeNavCls = cls

class BasePage(Base):
    ...
    # redacted because it's huge

同样,如果我尝试以非常简化的形式重新创建上述代码,mypy 不会出错。

typeB = TypeVar('typeB', bound='B')
tAlias = Type[typeB]

class Nav:
    def __init__(self, cls: tAlias):
        self.cls: tAlias = cls

class A: pass
class B(A): pass

【问题讨论】:

  • typeBaseTab 还是typeBasePage?您发布的内容在内部看起来不一致,不清楚这是否也是您的实际代码中的问题。
  • 抱歉.. 尝试不同的变体时出现错误。我修复了代码。应该是typeBasePage

标签: python python-3.x type-hinting mypy


【解决方案1】:

如果没有完整的重现,恐怕很难确定问题所在。我认为这很可能是一个 mypy 错误:mypy 可能会被代码中的某些循环弄糊涂。也许是一个导入周期,一些涉及模块和子模块导入的怪异,你的 mypy 缓存不知何故损坏了......

另一种可能性是您的代码在某种程度上不是以某种其他方式进行类型安全的,而这个错误只是 mypy 试图继续努力的下游结果。

为帮助缩小问题范围,我建议在确保您使用的是最新版本并删除 .mypy_cache 文件后重新运行 mypy。如果问题仍然存在,则可能值得尝试稳步删除代码库中不相关的部分以尝试梳理出重现。


也就是说,在这里改用完全不使用类型别名可能也值得:实际上,在示例中使用别名/类型 var 并没有获得任何好处。

简而言之,如果您在类型别名中使用任何类型变量,这些类型变量实际上总是空闲:当您开始使用别名时,您需要将它们显式绑定到某个类型。因此,与其使用def __init__(self, cls: typeNavCls),不如使用def __init__(self, cls: typeNavCls[SomeTypeHere])

否则,mypy 会将您的签名视为与def __init__(self, cls: typeNavCls[Any]) 完全相同,就像def foo(x: List) 的签名被视为def foo(x: List[Any])

您可以配置 mypy 以通过使用--strict 标记或--disallow-any-generics 标志运行它来警告您此问题:前一个标志自动启用后者。

相反,您可能应该执行以下操作之一:

# Approach 1: If you don't want Nav to be inherently generic
class Nav:
    def __init__(self, cls: Type[BasePage]):
        self.cls = cls

# Approach 2: If you want Nav to be inherently generic
_TBasePage = TypeVar('_TBasePage', bound='BasePage')
class Nav(Generic[_TBasePage]):
    def __init__(self, cls: Type[_TBasePage]):
        self.cls = cls

# Approach 3: If you want to ensure two or more types are the same,
# but nothing else
class Nav:
    def __init__(self, cls: Type[_TBasePage], instance: _TBasePage):
        # ...but you won't be able to save 'cls' as a field here: your
        # Nav class needs to be generic with respect to _TBasePage if you
        # want to "preserve" whatever type that's bound to as a field.

# Approach 4: If your alias is complex enough where you want to
# force a relationship between two or more types within the alias
MyAlias = Tuple[Type[_TBasePage], _TBasePage]
class Nav:
    def __init__(self, info: MyAlias[BasePageSubtype]):
        self.info = info

# Approach 5: ...or if you want Nav to also be generic:
MyAlias = Tuple[Type[_TBasePage], _TBasePage]
class Nav(Generic[_TBasePage]):
    def __init__(self, info: MyAlias[_TBasePage]):
        self.info = info

# Important note: the previous example is actually exactly
# equivalent to doing:

_T1 = TypeVar('_T1', bound=BasePage)
MyAlias = Tuple[Type[_T1], T1]

_T2 = TypeVar('_T2', bound=BasePage)
class Nav(Generic[_T2]):
    def __init__(self, info: MyAlias[_T2]):
        self.info = info

方法 1、2 和 3 应该回避您遇到的原始问题。如果这些方法与您实际想要做的类似,那么根本不使用别名会更简单/更简洁。如果你不使用别名,那么你就不会遇到这个错误。

但如果您的实际代码更像方法 4 或 5,不幸的是,可能需要进行更深入的调查。


关于您的self-answer here 的最后一点说明:文档的该部分是指在文字类型的上下文中使用问号只是。但是您没有使用 Literal 类型,因此文档的该部分与此处无关。

如果问题出现在常规实例之后,则表示该类型实际上是“未绑定类型”。这是一种特殊的内部类型,mypy 用作占位符,用于表示应该是类型但找不到对应定义的事物。

这要么意味着 mypy 中存在错误,导致 Unbound 类型出现在不应该出现的位置,要么意味着存在一些其他可能的合法错误,污染了下游错误。

【讨论】:

    【解决方案2】:

    我相信我找到了问号是什么,但离弄清楚为什么在 mypy 中发生错误还差得远。

    https://mypy.readthedocs.io/en/latest/literal_types.html

    如果您没有在Final 中提供显式类型,则c 的类型 变得上下文相关:mypy 基本上会尝试“替换” 在执行类型之前使用的原始分配值 检查。这就是为什么c 的显示类型是Literal[19]?: 最后的问号反映了这种上下文相关的性质。

    【讨论】:

      猜你喜欢
      • 2020-03-06
      • 2016-02-23
      • 2019-03-17
      • 2012-03-10
      • 2018-07-09
      • 2020-02-23
      • 2021-10-21
      • 1970-01-01
      • 2021-05-26
      相关资源
      最近更新 更多