2022-04-02

TypedDict in Python

What is TypedDict

TypedDict is a feature introduced in Python 3.8, as part of the typing module. It allows you to specify type hints for dictionaries, where you can denote expected keys and their corresponding value types. TypedDict does not enforce these types at runtime; instead, it's used for static type checking tools, improving the readability and maintainability of your code.

Creating TypedDict

Creating a TypedDict in Python is straightforward. First, you need to import the TypedDict class from the typing module. Then, you define your TypedDict as a new class that inherits from TypedDict. Inside this class, you define the keys and their associated types.

Let's create a simple TypedDict for a user's profile:

python
from typing import TypedDict

class UserProfile(TypedDict):
    name: str
    age: int
    email: str

In the example above, we have created a TypedDict named UserProfile. According to its definition, a dictionary of type UserProfile should have keys name, age, and email, with their respective types being str, int, and str.

Now that we've defined our UserProfile TypedDict, let's see how to use it:

python
def display_user(profile: UserProfile) -> None:
    print(f"Name: {profile['name']}")
    print(f"Age: {profile['age']}")
    print(f"Email: {profile['email']}")

user: UserProfile = {
    'name': 'Alice',
    'age': 30,
    'email': 'alice@example.com'
}

display_user(user)

In the code above, we first define a function display_user that takes a UserProfile dictionary as an argument and prints out the user's information. Then, we create a user dictionary according to the UserProfile TypedDict we defined earlier, and call display_user with user as the argument.

The type hint in the function signature helps ensure that the display_user function always receives the correct type of dictionary. If we try to call display_user with a dictionary that doesn't conform to the UserProfile TypedDict (for example, if it's missing a key or a key has a value of the wrong type), we'll get a type error.

Optional Keys in TypedDict

While the TypedDict we created in the previous sections required all keys to be present and of a certain type, it's also possible to define TypedDicts with optional keys. This can be done using the total parameter in the TypedDict definition.

Consider a scenario where an email address is optional in the user's profile. We can represent this by setting total=False in our UserProfile definition:

python
from typing import TypedDict

class UserProfile(TypedDict, total=False):
    name: str
    age: int
    email: str

Now, a UserProfile can either have an email key or not, and it will still be valid:

python
def display_user(profile: UserProfile) -> None:
    print(f"Name: {profile['name']}")
    print(f"Age: {profile['age']}")
    if 'email' in profile:
        print(f"Email: {profile['email']}")

user: UserProfile = {
    'name': 'Alice',
    'age': 30
}

display_user(user)

In this code, we've modified the display_user function to only print the email address if it's present in the profile. We then create a user dictionary without an email address, and display_user(user) runs without any errors.

Nested TypedDicts

TypedDicts can also be nested to represent more complex data structures. Consider a scenario where the user profile includes an address, which itself is a dictionary with several fields:

python
from typing import TypedDict

class Address(TypedDict):
    street: str
    city: str
    state: str
    zip_code: str

class UserProfile(TypedDict):
    name: str
    age: int
    email: str
    address: Address

In this code, we've defined an Address TypedDict, and then used it in the definition of UserProfile. Now a UserProfile includes an address key, whose value should be an Address dictionary.

TypedDict and Inheritance

TypedDicts also support inheritance. This can be useful when you have several types of dictionaries that share some fields but also have their own unique fields.

For instance, consider a scenario where, in addition to user profiles, we also have admin profiles. Admins have all the same fields as users, but they also have an additional permissions field:

python
class AdminProfile(UserProfile):
    permissions: list[str]

In this code, we define AdminProfile as a subclass of UserProfile, and add an additional permissions field. Now an AdminProfile is expected to have all the fields of a UserProfile, plus a permissions field.

References

https://peps.python.org/pep-0589/

Ryusei Kakujo

researchgatelinkedingithub

Focusing on data science for mobility

Bench Press 100kg!