I'm trying to type a function that returns a dictionary with one required key, and some additional ones.
I've run into TypedDict, but it is too strict for my purpose. At the same time Dict is too lenient.
To give some examples with what I have in mind:
class Schema(PartiallyTypedDict):
name: str
year: int
a: Schema = {"name": "a", "year": 1} # OK
b: Schema = {"name": "a", "year": 1, rating: 5} # OK, "rating" key can be added
c: Schema = {"year": 1, rating: 5} # NOT OK, "name" is missing
It would be great if there was also a way of forcing values of all of the optional/arbitrary key to be of a specific type. For example int
in example above due to "rating" being one.
Does there exist such a type, or is there a way of creating such a type in python typing framework?
Since PEP 655 there is a solution for this problem. In Python 3.11+ there are typing.Required
and typing.NotRequired
.
This means we have two ways to do this.
The typing.Required type qualifier is used to indicate that a variable declared in a TypedDict definition is a required key.
This means we can use the totality flag to mark any keys as not required. And then use the typing.Required
to mark explicitly keys as required.
from typing import TypedDict, Required
class Schema(TypedDict, total=False):
name: Required[str]
year: Required[int]
rating: int
the typing.NotRequired type qualifier is used to indicate that a variable declared in a TypedDict definition is a potentially-missing key
This means we can use the totality flag to mark any keys as required and use typing.NotRequired
to mark explicitly some keys as not required.
from typing import TypedDict, NotRequired
# total=True is the default value and therefore can be omitted.
# class Schema(TypedDict, total=True):
class Schema(TypedDict):
name: str
year: int
rating: NotRequired[int]
Both approaches enforce dicts to have a name and a year key. It also enforces that, if a rating key is in the dict, the value is of type int.
There is no restriction in adding further keys and there is no value-type restriction for those keys.
This means:
# OK
a: Schema = {"name": "a", "year": 1}
# OK, "rating" key can be added
b: Schema = {"name": "a", "year": 1, rating: 5}
# NOT OK, "name" is missing
c: Schema = {"year": 1, rating: 5}
# NOT OK, "rating" is no int
d: Schema = {"name": "a", "year": 1, "rating": "invalid type"}
# OK, because any (undefined) key can be added, with any type
e: Schema = {"name": "a", "year": 1, "rating": 5, "undefined-key": "undefined-value-type" }
Just for completeness:
There should be soon a way, to give type restrictions to arbitrary keys. PEP 728 brings up the extra_items
parameter for TypedDict.
So it should be possible to do something like:
from typing import TypedDict
class Schema(TypedDict(extra_items=int)):
name: str
year: int
Which should result in:
# Ok
a: Schema = {"name": "a", "year": 1, rating: 5}
# Ok
b: Schema = {"name": "a", "year": 1, rating: 5, another_arbitrary_key: 4}
# NOT Ok, because the arbitrary rating key is no int
a: Schema = {"name": "a", "year": 1, rating: "five"}
You may have expected the use of "should" this is, because at the time of writing this addendum the PEP 728 is not yet implemented.