静态类型很棘手。mypy
可以确定的值dictionary
并非都具有相同的类型,但仅此而已。这static类型dictionary
is Dict[str,object]
,基于初始值。然而,mypy
不会尝试进一步模拟代码,这意味着它不知道是否d['sub_dict']
is still其他dict
在您尝试对其进行索引的位置key2
,这会导致类型错误。
你能做的一件事就是提供帮助mypy
通过告诉它一个特定的值可以被视为具有特定的类型,使用typing.cast
.
print(typing.cast(typing.Dict[str,dict], d['sub_dict'])['key2'])
在运行时,typing.cast
实际上是一个恒等函数;它只返回第二个参数。mypy
将其视为更强的类型提示,表示无论之前的任何提示或注释如何,d['sub_dict']
应被视为Dict[str,dict]
.
但请注意,通过使用cast
,你正在告诉mypy
that you正在承担责任以确保dictionary['sub_dict']
事实上,是一个dict
在运行时,因为这不是可以用静态类型传达的东西。你可能会认为类似
dictionary : Dict[str,Union[int,dict]] = ...
会起作用,但这只是说明mypy
写起来会出现类型错误dictionary['foo'] = 'bar'
, since 'bar'
既不是int
or a dict
。即使有更准确的类型提示,仍然没有办法mypy
知道什么类型的值dictionary
将任何特定键映射到。
你可以使用Any
还有:
dictionary: Dict[str,Any] = ...
因为现在你说任何类型都可以用作值,并且any可以为索引结果假定类型,并且这两种类型不必对齐。那是,dictionary['key1'] = 3
很好,因为int
兼容于Any
, but dictionary['sub_dict']['key2']
也很好,因为无论如何dictionary['sub_dict']
产生的是also兼容于Any
,并且您可以假设该类型本身是可索引的。实际上,它涵盖了any use of dictionary
代码中的任何位置,而不是您使用的特定位置cast
就应该允许什么做出断言。
主要题外话:有一个概念依赖类型,最简单的例子是这样的类型PositiveInt
这将等同于int
除非它不允许负值。dictionary
似乎具有类似的依赖类型,其中值的类型实际上是存储在值中的实际数据的函数。例如,想象一下如果您可以使用instance of dict
with Dict
指定其值的类型。
dictionary: Dict[str, {"key1": int, "sub_dict": dict}] = {'key1': 1,
'sub_dict': {'key2': 0}
}
现在,不仅可以mypy
告诉那dictionary['key1']
应该是一个int
,但是那个dictionary
本身永远不可能有任何钥匙other than key1
and sub_dict
。 (在这个假设的世界中,defaultdict
可以将任意未指定的键映射到默认类型。)