Mypy不支持的类型Type [typeVarAlias]

时间:2019-11-19 19:14:35

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

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

有人知道这个错误是什么意思吗?问号具体表示什么?

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

相关代码:

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

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

microdnf

2 个答案:

答案 0 :(得分:1)

如果没有完整的复制程序,恐怕很难确定问题所在。我认为这很可能是mypy错误:mypy可能会由于代码中的某些循环而感到困惑。也许是导入周期,有些怪异,涉及模块和子模块的导入,您的mypy缓存以某种方式损坏了...

另一种可能性是您的代码以某种方式不能以安全的方式键入,并且此错误只是mypy试图继续奋斗的下游结果。

为帮助缩小问题范围,建议您在确保使用最新版本并删除.mypy_cache文件后重新运行mypy。如果问题仍然存在,则值得尝试稳定地删除代码库中无关的部分,以尝试找出一个再现对象。


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

简而言之,如果您在类型别名中使用任何类型变量,则这些typevar实际上总是 free :在开始使用别名时,需要将它们明确绑定到某种类型。因此,您不想做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的最后一条注释:文档的这一部分是指在文字类型的上下文中使用问号 just 。但是您没有使用文字类型,因此此处的文档部分与此无关。

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

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

答案 1 :(得分:0)

我相信我发现了问号是什么,但距离弄清楚为什么在mypy中发生错误的原因还很遥远。

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

  

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