Dictionary types in Python
Juan S. Díaz
· 4 min readI could list a lot of reasons why Python type hinting feature1 is one of the most
valuable features that this language has. Type hinting is the optional syntax
to describe types in variables and functions using typing
module. It’s available from
Python 3.5 (or higher) without any additional package.
Across newer Python versions, some types could be used without typing
explicit import so
the linters could give you more information about simpler way to use type hinting for your
Python version:
Anyway, defining a dictionary using basic type is too general and you could be interested in be more precise than this:
Keep in mind that Python typing will not force you or throw execution errors due to unexpected responses, but linters will warn you about incorrect access to attributes. We could talk about Python linters in another opportunity.
Advantages of a programming language with explicit types are the autocompletion and improved linting, very useful for giving context faster to other programmers and, obviously, AI tools for better suggestions.
Alternatives for dictionary typing
TypedDict
Check this example based on Harry Potter API.
Let get_user()
method returns a dict:
Character metadata is bigger than example fields but you could define as many as you need. Also, you can add pydocs to explain fields.
I highly recommend this approach when you define types for third party API responses due to schema variability. Some APIs could include a Python package with its schema types but most of those integrations don’t do it.
Some considerations:
- TypedDict represents a
dict
. - TypedDict is mutable (e.g.
harry["name"] = "New Name"
is valid). - TypedDict is extensible (e.g.
harry["name_2"] = "New Name"
is valid).
NamedTuple
It’s a little more tricky than TypedDict due to longer value assignation but it returns an immutable object.
It’s a syntax sugar for collections.namedtuple
and it’s very useful for application DTOs2
and any other object for read-only purposes. Also, dot notation is more strict
to get execution errors.
Some considerations:
- NamedTuple represents a
tuple
. - NamedTuple is immutable (e.g.
harry.name = "New Name"
is not valid). - NamedTuple is not extensible (e.g.
harry.name_2 = "New Name"
is not valid).
dataclasses
If you want to aggregate behavior and properties all-in-one, you could use @dataclass
decorator
instead of previous ones.
This option actually adds methods to a class3 without changing its meaning like TypedDict and NamedTuple does. It could help a lot in frameworks and Python packages development and full object-oriented programming.
Some considerations:
- Dataclass represents a
class
. - Dataclass is mutable (e.g.
harry.name = "New Name"
is valid). - Dataclass is extensible (e.g.
harry.name_2 = "New Name"
is valid).
Dataclasses could be immutable if you add frozen=True
parameter to @dataclass
decorator.
Conclusions
The following table could summary this article:
Comparison | TypedDict | NamedTuple | dataclass |
---|---|---|---|
It behaves like… | dict | tuple | class |
Recommended for… | 3rd party responses | Read-only data and DTOs | Frameworks and packages using OOP |
Is mutable? | ✅ | ❌ | ✅ |
Is extensible? | ✅ | ❌ | ✅ |
References
- https://peps.python.org/topic/typing/
- https://docs.python.org/3/library/typing.html
- https://docs.python.org/3/library/dataclasses.html#module-contents
- https://hp-api.onrender.com/
- https://www.geeksforgeeks.org/typing-namedtuple-improved-namedtuples/
Footnotes
-
It was added on PEP 484. You can get more info about Python typing future from PEP Index. ↩
-
Data Transfer Objects are object definitions for read-only and serializable data used by the processes into the application (like the database items). It’s a solution to standardize sent data of the processes. ↩
-
According official documentation, that decorator generates
__init__
constructor based on attributes and it could include some basic comparison methods. ↩