对于使用 `type()` 构造的类型,mypy“作为类型无效”

Posted

技术标签:

【中文标题】对于使用 `type()` 构造的类型,mypy“作为类型无效”【英文标题】:mypy "is not valid as a type" for types constructed with `type()` 【发布时间】:2020-02-23 22:34:46 【问题描述】:

mypy 抱怨error: Variable "packagename.Foo" is not valid as a type

Foo = type('Foo', (), )
Bar = Optional[Foo]

可以通过将类型定义为类来修复此错误:

class Foo:
    pass

Bar = Optional[Foo]

还有其他方法可以解决这个问题吗?我需要保持类型定义动态。

【问题讨论】:

为什么要这样使用type()?请注意,mypy 不会运行/评估代码,所以我认为不会执行 type 来“创建新类型对象” 我猜这是因为 mypy can't handle dynamic base classes. 一般来说,静态类型检查器无法处理动态创建的类型。 我不太明白的是,如果你“承诺”类型检查器你的变量将是类类型a: Type[Foo] = Foo,为什么它不认为是有效的class Bar(a)?我们可以在 TypeScript 中毫无问题地做到这一点:typescriptlang.org/play/#code/… 我打算建议不同之处在于class,作为一个句法结构,必须产生一个类型,而type可以反弹到任何可调用的运行。但是,也可以通过指定不同的元类来修改 class 语句。最后,请记住,mypy 确实是一个 hack(从最好的意义上说)试图从 Python 所基于的固有动态数据模型中挤出尽可能多的静态类型。它必须解决许多极端情况。 【参考方案1】:

作为一种解决方法,这个怎么样?

from typing import Optional, TYPE_CHECKING

if TYPE_CHECKING:
    class Foo: pass
else:
    Foo = type('Foo', (), )
    
Bar = Optional[Foo]

typing.TYPE_CHECKING 是一个常量,在编译时始终为True,在运行时始终为False。通过这种方式,我们可以只告诉 MyPy 静态定义,但在运行时我们可以随心所欲地动态。

不过,您应该知道,这在很大程度上是一种解决方法,而不是解决方案。通过这种方式,我们实质上是在向类型检查器撒谎Foo 的真实定义。这意味着 MyPy 可能无法发现某些地方的错误,并且可能会引发其他地方不存在的错误。在某些情况下,在运行时动态构造类型非常有用,但它违反了 Python 中类型检查的一些基本原则,因此如果没有某种 hack,你将很难让类型检查器批准你正在做的事情。

【讨论】:

在我的用例中,我有一个返回类 (var=class_factory()) 的类工厂函数。这是我能找到的唯一方法让 mypy 接受 var 作为一个类。谢谢!【参考方案2】:

这对你有用吗?

from typing import TypeVar, Optional

Foo = TypeVar('Foo')
Bar = Optional[Foo]

【讨论】:

这与问题中已经提出的class Bar: 方法有何根本不同?使这项工作起作用的部分是它使用了class 语句——在顶部添加dataclass 不会影响这一点。 所以这里要创建动态类型,如果你用class关键字定义类型有什么不容易的? 恐怕您的编辑仍然无法解决 OP 的原始问题:/ TypeVar not 是否以与调用 type 相同的方式创建一个新类用三个参数就可以了。 TypeVar 文档。 type 文档。

以上是关于对于使用 `type()` 构造的类型,mypy“作为类型无效”的主要内容,如果未能解决你的问题,请参考以下文章

如何为 Mypy 类型注释指定 OrderedDict K,V 类型?

mypy 错误 - 尽管使用了“Union”,但类型不兼容

如何获取 MyPy 的正则表达式模式类型

类装饰器上的 Mypy 注释

mypy:如何最好地处理 random.choice

使用 mypy 检查类型时有啥问题