Python 3 dictionary with known keys typing
Asked Answered
R

2

98

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?

Redaredact answered 28/5, 2017 at 9:32 Comment(7)
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.Olecranon
You can also try to use Dict[str, str] type hint from typing module.Wynnie
@Olecranon thanks for remind me this option.Found exactly what i need. There is type named tuple docs.python.org/3/library/typing.html#typing.NamedTupleRedaredact
See also: https://mcmap.net/q/218781/-how-to-type-hint-a-dictionary-with-values-of-different-types-duplicate/6646912Alinealinna
Related: How to use static type checking using Dict with different value types in Python 3.6?Corneliuscornell
This is a good use case for @attrs : attrs.org/en/stable . Just define your object as an attrs-class and then attr.asdict(name_info_pair) will give you the desired dictAnteversion
..or, if you're on Python 3.7+ and don't want an external dependency on attrs, you can use data classes.Smashandgrab
C
119

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]}
Corneliuscornell answered 28/5, 2017 at 9:32 Comment(5)
In this way you get some class-specific limitations, for example you can not pack your class using msgpack.Weitzman
how to define, that NameInfo has key with service symbols, for example last#name: str?Paterfamilias
@IvanBryzzhin You might try using alternative syntax.Corneliuscornell
This only works for str keys thoughFelicitous
@Felicitous you can, for the value, use Literal instead of str to give you a type hint supportResiduary
I
14

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(). This allows you to create keys that uses characters not in A-Za-z0-9_.

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 (also works on jupyter notebook too by pressing tab).

pycharm

Intracutaneous answered 17/6, 2023 at 2:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.