我正在使用Python 3.6.1,mypy和输入模块。我创建了两个自定义类型Foo
和Bar
,然后在函数返回的dict中使用它们。 dict被描述为将str
映射到Union
和Foo
的{{1}}。然后我想在一个只命名一个参数的函数中使用这个dict中的值:
Bar
我尝试使用原样:
from typing import Dict, Union, NewType
Foo = NewType("Foo", str)
Bar = NewType("Bar", int)
def get_data() -> Dict[str, Union[Foo, Bar]]:
return {"foo": Foo("one"), "bar": Bar(2)}
def process(foo_value: Foo, bar_value: Bar) -> None:
pass
d = get_data()
或使用类型:
process(d["foo"], d["bar"])
# typing-union.py:15: error: Argument 1 to "process" has incompatible type "Union[Foo, Bar]"; expected "Foo"
# typing-union.py:15: error: Argument 2 to "process" has incompatible type "Union[Foo, Bar]"; expected "Bar"
如何将process(Foo(d["foo"]), Bar(d["bar"]))
# typing-union.py:20: error: Argument 1 to "Foo" has incompatible type "Union[Foo, Bar]"; expected "str"
# typing-union.py:20: error: Argument 1 to "Bar" has incompatible type "Union[Foo, Bar]"; expected "int"
转换为其子类型之一?
答案 0 :(得分:3)
您必须使用cast()
:
process(cast(Foo, d["foo"]), cast(Bar, d["bar"]))
来自PEP 484的 Casts 部分:
类型检查器有时可能需要不同类型的提示:程序员可能知道表达式的类型比类型检查器可能推断的更具约束性。
没有办法拼写具体类型的值与字典键的具体值有什么关系。您可能需要考虑返回named tuple,而不是键入每个键:
from typing import Dict, Union, NewType, NamedTuple
Foo = NewType("Foo", str)
Bar = NewType("Bar", int)
class FooBarData(NamedTuple):
foo: Foo
bar: Bar
def get_data() -> FooBarData:
return FooBarData(foo=Foo("one"), bar=Bar(2))
现在,类型hinter知道完全每个属性类型是什么:
d = get_data()
process(d.foo, d.bar)
答案 1 :(得分:1)
虽然我认为演员阵容可能是您案例中使用的正确选项,但我只想简单地提及一个可能适用于类似场景的其他选项,以解决问题:
实际上可以使用新的实验性TypedDict功能更准确地输入您的词典,该功能是最新版本的mypy(如果您从github repo克隆的话)并且可能会在下一个pypi发布。
要使用TypedDict,您需要运行pip install mypy_extensions
从pypi安装mypy_extensions
。
TypedDict允许您为dict中的每个项目分配单独的类型:
from mypy_extensions import TypedDict
Foo = NewType("Foo", str)
Bar = NewType("Bar", int)
FooBarData = TypedDict('FooBarData', {
'foo': Foo,
'bar': Bar,
})
您还可以在Python 3.6 +中使用基于类的语法定义FooBarData
:
from mypy_extensions import TypedDict
Foo = NewType("Foo", str)
Bar = NewType("Bar", int)
class FooBarData(TypedDict):
foo: Foo
bar: Bar
你还提到你的dict可以拥有动态数量的元素。如果它真的是动态的,那么TypedDict会因为NamedTuple没有帮助的原因而获得帮助,但是如果你的TypedDict最终会有一个有限的元素数量,而你只是逐步添加项目而不是一次性,您可以尝试使用non-total TypedDicts,或尝试构建mix required and non-required items的TypeDicts。
值得注意的是,与其他几乎所有类型不同,TypedDicts使用结构类型进行检查,而不是标称类型。这意味着,如果您定义一个名为完全无关的TypedDict,例如QuxData
,其foo
和bar
字段的类型与FooBarData
相同,那么{{1}实际上将是QuxData
的有效子类型。这可能会带来一些有趣的可能性,但有点聪明。