How to set the python type hinting for a dictionary variable?
Asked Answered
S

3

89

Let say I have a dictionary:

from typing import Dict

v = { 'height': 5, 'width': 14, 'depth': 3 }

result = do_something(v)

def do_something(value: Dict[???]):
    # do stuff

How do I declare the dictionary type in do_something?

Satyriasis answered 1/10, 2018 at 14:55 Comment(3)
IIUC, see hereAsco
I believe it's Dict[key_type, value_type], so in your case Dict[str, int]Lanfri
TypedDict, check PEP 589Carnet
H
112

Dict takes two "arguments", the type of its keys and the type of its values. For a dict that maps strings to integers, use

def do_something(value: Dict[str, int]):

The documentation could probably be a little more explicit, though.

Halftruth answered 1/10, 2018 at 15:0 Comment(8)
Thanks, Chepner, but what would I do if the dictionary also had a key such as 'name': 'Book', i.e. a string key, and string value including the original values?Satyriasis
Then you would have to use a union type that could cover a string or an int. Dict[str, Union[str, int]].Halftruth
I'm on python 3.7 and I was struggling with below type hint until I saw this post and realized that I need to use 'Dict' instead of 'dict': ~ErrorClass = Literal["Info", "Warning", "Error"] Score = Dict[ErrorClass, float]~Agraphia
how do i write typing if have multiple keys and values, with keys and values of different types for a dict like: {'a':1, b'abc':[1.0,2.0,3.0],23:['x','y','z']}.types are :"{str:int, bytes-string:list(floats), int: list(str)}"Merrileemerrili
dict/Dict can't handle that case. You would need to use TypedDict (which itself is limited to dicts with str keys but arbitrary types for each value).Halftruth
@Agraphia If you haven't noticed yet (and can take advantage), Python 3.9 makes dict generic, so dict[ErrorClass, float] would now work. Dict is now deprecated (though I have no idea if there is a timeline for actually removing it).Halftruth
Minor nitpick: the docs for typing.Dict recommend using Mapping[] for arguments and Dict[] for return types, so Mapping[str, int] might be even more appropriate here.Disband
That makes sense: broad arguments and narrow return types work better with sub typing.Halftruth
K
65

Python 3.9 on:

Use lowercase dict in the same method as the accepted answer. typing.Dict and similar upper case generic types which mirror built-ins are deprecated due to PEP 585:

def my_func(value: dict[str, int]):
    pass
Khelat answered 24/8, 2021 at 13:26 Comment(5)
This is great. Dict and List are ugly and remind me of Java.Heliometer
@Heliometer I WISH more of python reminded me of Java... :D Changing the case of a reference class to lower case because it is prettier rather than referencing the class that defines the type you wish to type hint seems like a step backwards.Hunan
@Hunan 'dict' and 'list' are actually the class names. I believe that's to distinguish built-ins from non-built-ins as apposed to primitive vs non primitive. User-defined types are typically pascal case. Python likely derived this naming scheme from C or C++. In C structs are typically lower case (though there are a lot of people that use pascal case), and in C++ people typically use pascal, but all standard library types are lowercase.Khelat
@HunterKohler huh. So they are. I see. Thanks for clarifying. I should have read through the PEP 585 before commenting.Hunan
@Hunan Not having to type from typing import Dict is also quite nice in 3.9+.Meal
B
0

It depends on how well-defined your dictionary is.

Maybe there is a dictionary where you don't really know what it contains or will contain, but at least you know the keys should be string and the values should be boolean.

def do_something(value: dict[str, bool]):
    pass

However, perhaps you actually know very well exactly what keys it should have.

from __future__ import ReadOnly
from typing import Literal, NotRequired, TypedDict


class Movie(TypedDict):
    title: ReadOnly[str]
    year: NotRequired[ReadOnly[int]]


class RatedMovie(Movie):
    # also has fields of Movie
    star_rating: Literal[1, 2, 3, 4, 5]


def do_something(movie: Movie) -> None:
    t = movie["title"]  # we get decent IDE type-hinting here


# and here
movie: Movie = {
    "title": "The Princess Bride",
    "year": 1987,
}


# and here
do_something({"title": "Fight Club"})

See TypedDict.

ReadOnly planned v3.13 (not available). NotRequired from v3.11.


As an aside, I HIGHLY recommend NOT using total=False TypedDicts. This type is very vague and should only be used for dictionaries where every field is optional. Marking individual fields as NotRequired should be heavily favored. Using total=False and using Required is a strange inversion of logic that feels like an antipattern waiting to emerge, although I guess it could save you some typing :)

Bilabial answered 19/5 at 8:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.