首页 > 解决方案 > Mypy 不支持的类型 Type[typeVarAlias]

问题描述

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

标签: pythonpython-3.xtype-hintingmypy

解决方案


如果没有完整的重现,恐怕很难确定问题所在。我认为这很可能是一个 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,不幸的是,可能需要进行更深入的调查。


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

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

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


推荐阅读