问题描述
在Python中,我们可以通过这种方式给变量赋值,并通过mypy的类型检查:
from typing import Type,Union
class Foo:
pass
MyType: Type[Foo] = Foo
同样,我们也可以使用 Union
来输入
from typing import Type,Union
class Foo:
pass
class Bar:
pass
def func(is_bar: bool) -> Union[Type[Foo],Type[Bar]]:
if is_bar:
return Bar
else:
return Foo
知道 is_bar
的值,在调用 func
进行类型缩小后使用断言是完全合理的。
但是,断言似乎对 MyType 根本不起作用,因为 mypy 为以下代码示例提供错误:
MyType = func(True)
assert MyType is Bar
# `assert MyType is Type[Bar]` gives the same result
Test: Bar = MyType # Incompatible types in assignment (expression
# has type "Union[Type[Foo],Type[Bar]]",# variable has type "Bar")
cast
和 isinstance
也不起作用。
MyType = func(True)
cast(Type[Bar],MyType)
Test: Bar = MyType # MyType is considered `Type[Bar] | Type[Foo]`
MyType = func(True)
assert isintance(MyType,Type[Bar])
Test: Bar = MyType # MyType is considered `Type[Bar] | Type[Foo]`
我的问题是:如何对类型的类型进行类型缩小? 或者是mypy的限制?如果是这样,解决方法是什么?
相关:
- [1] stackoverflow - type-assertion-in-mypy
- [2] Mypy doc - casts
- [3] Mypy doc - kinds of types (the type of class objects)
解决方法
cast
帮助器返回它的受约束参数,它实际上并没有改变它的参数被约束。
cast
到所需的 Type[...]
并分配或使用结果:
Test = cast(Type[Bar],MyType)
reveal_type(Test) # note: Revealed type is "Type[so_testbed.Bar]"
,
另一种避免必须使用 typing.cast
或 isinstance
的解决方案是使用 typing.overload
,它允许您注册单个函数的多个签名。所有用 @typing.overload
修饰的函数在运行时都会被忽略,以支持“具体”实现,因此这些函数的主体可以只是一个文字省略号。通过将 typing.overload
与 typing.Literal
结合,如果传入值 True
,我们可以注册函数的一个签名,如果传入值 False
,我们可以注册另一个:>
from typing import Type,Union,overload,Literal
class Foo:
pass
class Bar:
pass
@overload
def func(is_bar: Literal[True]) -> Type[Bar]: ...
@overload
def func(is_bar: Literal[False]) -> Type[Foo]: ...
def func(is_bar: bool) -> Union[Type[Foo],Type[Bar]]:
if is_bar:
return Bar
else:
return Foo
test: Type[Bar] = func(True)
test2: Type[Foo] = func(False)
test3: Bar = func(True)()
test4: Foo = func(False)()
它的类型检查很好。