如何在Mypy类型注释中指定OrderedDict的K、V类型?

52
我正在使用Python 3.5和Mypy进行基本静态检查。最近,我重构了一些方法以返回OrderedDict,但在尝试使用指定键和值类型的返回注释时遇到了“'type' object is not subscriptable”错误。
简化示例:
#!/usr/bin/env python3.5

from collections import OrderedDict

# this works
def foo() -> OrderedDict:
    result = OrderedDict() # type: OrderedDict[str, int]
    result['foo'] = 123
    return result

# this doesn't
def foo2() -> OrderedDict[str, int]:
    result = OrderedDict() # type: OrderedDict[str, int]
    result['foo'] = 123
    return result

print(foo())

当运行时,这是Python的输出:

Traceback (most recent call last):
  File "./foo.py", line 12, in <module>
    def foo2() -> OrderedDict[str, int]:
TypeError: 'type' object is not subscriptable

Mypy并不会因为注释中的类型注释出现问题,事实上,如果我尝试执行“result [123] = 123”操作,它将发出警告。这是什么原因?

1
现在它可以工作了(mypy版本0.501)。 - max
1
只是放置一个修复链接:https://bugs.python.org/issue35341 - Samuel Harmer
5个回答

38

mypy没有问题(至少在0.501版本中没有)。 但是Python 3.6.0存在一个问题。 请考虑以下内容:

from collections import OrderedDict
from typing import Dict

def foo() -> Dict[str, int]:
    result: OrderedDict[str, int] = OrderedDict()
    result['two'] = 2
    return result

这段代码既能满足 mypy (0.501) 又能满足 Python (3.6.0)。但是,如果你将 Dict 替换为 OrderedDict,那么 mypy 仍然会很高兴,但执行它时会出现 TypeError: 'type' object is not subscriptable 的错误。

有趣的是,Python 解释器在函数签名中看到一个有下标的 OrderedDict 后就会停止工作,但它可以接受变量类型注释中的这种写法。

总之,我的解决方法是在函数签名中使用 Dict 而不是 OrderedDict(并添加一条注释,如果/当 Python 解释器学会接受正确的函数签名时,应该进行修复)。


1
是的,最终我在自己的代码中使用了相同的解决方法。 - Xarn
这导致了在 mypy 0.063 中出现错误,但建议将函数签名更改为字符串的答案有效。 - YPCrumble
11
Python 3.7.2引入了typing.OrderedDict - majkelx
3
在3.7.2中引入的typing.OrderedDict在3.9中再次被弃用,而新增了对collections.OrderedDict[K, V]的支持。 - jlh
另一个解决方法是在函数签名中引用类型,例如:'OrderedDict [...] ' - levsa
使用这些评论来表达我的抱怨,即我无法将collections.OrderedDicttyping.OrderedDict同时导入到同一命名空间中,例如from typing import OrderedDict; from collections import OrderedDict - CodeJockey

17

解决方法是,您还可以将返回类型放入字符串中,以满足Mypy和Python 3.6:

作为解决方案,您还可以将返回类型放入字符串中,以同时满足Mypy和Python 3.6的要求。

from collections import OrderedDict

def foo() -> 'OrderedDict[str, int]':
    result = OrderedDict()
    result['foo'] = 123
    return result

6

collections.OrderedDict并不等同于typing.OrderedDict。

from collections import OrderedDict as collections_OrderedDict
from typing import OrderedDict

# this works
def foo() -> OrderedDict[str, int]:
    result = collections_OrderedDict()
    result['foo'] = 123
    return result

print(foo())

1
有道理,但为什么不直接 import collections; result=collections.OrderedDict() 呢? - CodeJockey

2

我不知道哪个版本允许这样做,但截至2021年3月24日,针对Python 3.7.5的更好解决方案如下:

from collections import OrderedDict
import typing

def foo() -> typing.OrderedDict[str, int]:
    result: typing.OrderedDict[str, int] = OrderedDict()
    result['two'] = 2
    return result

享受所有的世界!


对于3.7版本,我尝试使用suggestions: OrderedDict[str, Any] = OrderedDict(),但是在OrderedDict()下面有一个红色的波浪线,上面写着“应该通过方括号指定泛型”。但是仅仅将()替换为[]也无法解决问题,因为现在它需要在[]中放置一个表达式。不确定我是否理解了这个问题。 - perennial_noob
@perennial_noob 请粘贴出导致错误的代码。另外,您是否使用的是3.7.5+版本? - Gulzar
我实际上正在使用3.8.7。是的,所以我在使用Pycharm,并且它建议使用[]修复。我点击了它以让它修复,但现在它需要一个表达式。不确定是否应该使用None初始化值,例如suggestions: OrderedDict[str, Any] = OrderedDict[dummykey]。我甚至尝试过suggestions: OrderedDict[str, Any] = {},但是语法检查器警告说它期望OrderedDict,但实际得到的是Dict。 - perennial_noob
尝试使用 suggestions: OrderedDict[str, Any] = OrderedDict()suggestions[dummykey] = 1 - Gulzar

2

您还可以尝试使用MutableMapping(例如此答案中所示:https://dev59.com/61sW5IYBdhLWcg3woYni#44167921

from collections import OrderedDict
from typing import Dict

def foo() -> MutableMapping[str, int]:
    result = OrderedDict() # type: MutableMapping[str, int]
    result['foo'] = 123
    return result

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接