简体   繁体   中英

How do I specify OrderedDict K,V types for Mypy type annotation?

I am using Python 3.5 together with Mypy to have some basic static checking for my script. Recently I refactored some methods to return OrderedDict, but ran into "'type' object is not subscriptable" error, when I tried to use return annotation with Key and Value types specified.

Reduced example:

#!/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())

And this is python output when it is run:

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

Mypy however has no problem with the type annotation in comment and will in fact warn if I try to do result[123] = 123 .

What is causing this?

There is no problem in mypy (at least, not in 0.501). But there is a problem with Python 3.6.0. Consider the following:

from collections import OrderedDict
from typing import Dict

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

This code will both satisfy mypy (0.501) and Python (3.6.0). However, if you replace Dict with OrderedDict , then mypy will still be happy, but executing it will die with TypeError: 'type' object is not subscriptable .

It is interesting that the Python interpreter dies on seeing a subscripted OrderedDict in the function signature, but is happy to accept it in a variable type annotation.

At any rate, my workaround for this is to use Dict instead of OrderedDict in the function signature (and add a comment that this should be fixed if/when the Python interpreter will learn to accept the correct signature).

As a workaround, you can also put the return type into a string to satisfy both Mypy and Python 3.6:

from collections import OrderedDict

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

What you can also try is using MutableMapping (like in this Answer: https://stackoverflow.com/a/44167921/1386610 )

from collections import OrderedDict
from typing import Dict

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

I don't know which version allowed this, but a better solution as for March/24/2021, tested for 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

Enjoy all worlds!

collections.OrderedDict is not the same that 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())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM