我想知道如何(或者目前是否可能)表达一个函数将返回一个特定类的子类,这是我可以接受的?
下面是一个简单的例子,其中基类Foo
被Bar
和Baz
继承,并且有一个方便的函数create()
将根据指定的参数返回Foo
的子类(Bar
或Baz
):
class Foo:
pass
class Bar(Foo):
pass
class Baz(Foo):
pass
def create(kind: str) -> Foo:
choices = {'bar': Bar, 'baz': Baz}
return choices[kind]()
bar: Bar = create('bar')
使用mypy检查此代码时,返回以下错误:
错误:赋值类型不兼容(表达式类型为“Foo”,变量类型为“Bar”)
是否有方法表明这应该是可接受/允许的。create()
函数的预期返回不是(或可能不是)Foo
的示例,而是它的子类?
我在找这样的东西:
def create(kind: str) -> typing.Subclass[Foo]:
choices = {'bar': Bar, 'baz': Baz}
return choices[kind]()
但那是不存在的显然,在这个简单的例子中,我可以做到:
def create(kind: str) -> typing.Union[Bar, Baz]:
choices = {'bar': Bar, 'baz': Baz}
return choices[kind]()
但是我在寻找一个可以概括为N个可能子类的东西,其中N是一个比我想定义的typing.Union[...]
类型大的数字。
有人知道如何以非复杂的方式做到这一点吗?
在可能的情况下,没有非复杂的方法来做到这一点,我知道一些不太理想的方法来规避这个问题:
1.一般化返回类型:
def create(kind: str) -> typing.Any:
...
这解决了赋值时的类型问题,但这是一个麻烦,因为它减少了函数签名返回的类型信息。
1.忽略错误:
bar: Bar = create('bar') # type: ignore
这抑制了mypy错误,但这也不是理想的。我确实喜欢它,它使它更明确地表明,bar: Bar = ...
是故意的,而不仅仅是一个编码错误,但抑制错误仍然不太理想。
1.强制转换类型:
bar: Bar = typing.cast(Bar, create('bar'))
与前一个例子一样,这个例子的积极方面是它使Foo
返回到Bar
赋值更加明确。这可能是最好的选择,如果没有办法做到我上面的要求。我认为我讨厌使用它的部分原因是作为一个 Package 函数的笨拙(在使用和可读性方面)。可能只是现实,因为类型转换不是语言的一部分-例如create('bar') as Bar
,或create('bar') astype Bar
,或沿着的东西。
3条答案
按热度按时间hc8w905p1#
我并不是在抱怨你定义函数的方式:那部分实际上完全没问题,没有错误。
相反,它在抱怨你在最后一行的变量赋值中 * 调用 * 函数的方式:
由于
create(...)
被注解为返回Foo
或foo的任何子类,因此将其分配给Bar
类型的变量并不保证安全。您的选择是删除注解(并接受bar
将是Foo
类型),直接将函数的输出转换为Bar
,或者重新设计代码以避免此问题。如果你想让我明白,当你传入字符串
"bar"
时,create
将具体返回一个Bar
,你可以通过组合重载和Literal types来解决这个问题。例如,你可以这样做:但就我个人而言,我会对过度使用这种模式持谨慎态度--老实说,我认为频繁使用这种类型的恶作剧是一种代码气味。此解决方案也不支持对任意数量的子类型进行特殊封装:你必须为每一个创建一个重载变量,这可能会变得非常庞大和冗长。
eyh26e7m2#
2023年更新:mypy 1.3.0
根据PEP484,您需要执行以下操作:
有必要传入你想要示例化的类类型,这是有意义的,因为mypy是一个静态类型检查器,不做运行时类型检查。
也就是说,当输入是动态的时,将变量bar指定为
Bar
类类型没有帮助,可能会改为'baz'。另一方面,动态地选择要示例化的类类型是很有用的。因此,不要让它“看起来”是动态的,但仍然将变量类型硬编码为
Bar
,而是将变量类型指定为基类。然后,通过在运行时检查类类型来执行业务逻辑所需的任何操作:
原始答案(2021年3月)
你可以在这里找到答案。基本上,您需要执行以下操作:
o4hqfura3#
我用
typing.Type
解决了一个类似的问题。对于你的情况,我会这样使用它:这似乎是可行的,因为
Type
是“协变的”,尽管我不是Maven,但上面的链接指向PEP484以获得更多细节