84

I'm using Python 3 typing feature for better autocomplete.

Many times I have functions that return key/value (dictionary) with specific keys. super simple example:

def get_info(name):
    name_first_letter = name[0]
    return {'my_name': name, 'first_letter': name_first_letter}

I want to add type hinting to this function to tell others who use this function what to expect.

I can do something like:

class NameInfo(object):
    def __init__(self, name, first_letter):
        self.name = name
        self.first_letter = first_letter

and then change the function signature to:

def get_info(name) -> NameInfo:

But it requires too much code for each dictionary.

What is the best practice in that case?

cottontail
  • 10,268
  • 18
  • 50
  • 51
Idok
  • 3,642
  • 4
  • 21
  • 18
  • 2
    Have you considered using `collections.namedtuple` to create your custom types? It does make only immutable types, but depending on how you were using the dictionaries before, that might not be an issue. – Blckknght May 28 '17 at 09:48
  • 10
    You can also try to use `Dict[str, str]` type hint from [typing](https://docs.python.org/3/library/typing.html) module. – Stanislav Ivanov May 28 '17 at 10:08
  • 5
    @Blckknght thanks for remind me this option.Found exactly what i need. There is type named tuple https://docs.python.org/3/library/typing.html#typing.NamedTuple – Idok May 28 '17 at 10:38
  • 1
    See also: https://stackoverflow.com/q/51031757/6646912 – krassowski Jan 25 '19 at 00:02
  • Related: [How to use static type checking using Dict with different value types in Python 3.6?](https://stackoverflow.com/q/48013561/7851470) – Georgy Oct 10 '19 at 15:20
  • This is a good use case for @attrs : https://www.attrs.org/en/stable/ . Just define your object as an attrs-class and then `attr.asdict(name_info_pair)` will give you the desired dict – Peter Jun 12 '20 at 12:29
  • ..or, if you're on Python 3.7+ and don't want an external dependency on attrs, you can use [data classes](https://www.python.org/dev/peps/pep-0557/#abstract). – Nickolay Jun 17 '21 at 11:52

2 Answers2

103

As pointed out by Blckknght, you and Stanislav Ivanov in the comments, you can use NamedTuple:

from typing import NamedTuple


class NameInfo(NamedTuple):
    name: str
    first_letter: str


def get_info(name: str) -> NameInfo:
    return NameInfo(name=name, first_letter=name[0])

Starting from Python 3.8 you can use TypedDict which is more similar to what you want:

from typing import TypedDict


class NameInfo(TypedDict):
    name: str
    first_letter: str


def get_info(name: str) -> NameInfo:
    return {'name': name, 'first_letter': name[0]}
Georgy
  • 12,464
  • 7
  • 65
  • 73
Bernhard
  • 2,084
  • 2
  • 20
  • 33
3

TypedDict can also be created using a typename and a dictionary where the keys are the keys and the values are the type of the values, respectively, of the dict to be returned by get_info().

from typing import TypedDict

NameInfo = TypedDict('NameInfo', {'name': str, 'first-letter': str})

def get_info(name: str) -> NameInfo:
    return {'name': name, 'first-letter': name[0]}

x = get_info('cottontail')
print(x['first-letter'])   # c

Then on PyCharm, the key hint is there.

pycharm

cottontail
  • 10,268
  • 18
  • 50
  • 51