What are the main differences of NamedTuple and TypedDict in Python / mypy
Asked Answered
H

6

52

It seems to me that NamedTuple and TypedDict are fairly similar and the Python developers themselves recognized that.

Concerning the PEP, I would rather add a common section about NamedTuple and TypedDict, they are quite similar and the latter already behaves structurally. What do you think? source

But then Guido seems not so sure about that.

I'm not so sure that NamedTuple and TypedDict are really all that similar (except they are both attempts to handle outdated patterns in a statically-typed world).

source

So, this is my lazy attempt to get someone else come up with a crisp comparison where the official documentation seems lacking.

Hothouse answered 21/11, 2018 at 9:40 Comment(3)
do namedtuple & dict look similar to you?Middendorf
@AzatIbrakov That is a good point. To know the difference between NamedTuple and TypedDict one should first understand the difference between namedtuple and dict.Cytosine
What I would love to find is a NamedTuple/TypedDict/etc. alternative that is covariant rather than invariant. Something like a “TypedMapping” (in analogy to Mapping vs dict).Atthia
B
104

Python and its community are wrestling with the "struct" problem: how to best group related values into composite data objects that allow logical/easy accessing of components (typically by name). There are many competing approaches:

  • collections.namedtuple instances
  • dictionaries (with a fixed/known set of keys)
  • attribute-accessible dictionaries (like stuf)
  • the attrs library
  • PEP 557 dataclasses
  • plain old bespoke objects hand-crafted for every struct type
  • sequences like tuple and list with implied meanings for each position/slot (archaic but extremely common)
  • etc.

So much for "There should be one—and preferably only one—obvious way to do it."

Both the typing library and Mypy, like the Python community at large, are simultaneously struggling with how to more effectively define types/schema, including for composite objects. The discussion you linked to is part of that wrestling and trying to find a way forward.

NamedTuple is a typing superclass for structured objects resulting from the collections.namedtuple factory; TypedDict a Mypy attempt to define the keys and corresponding types of values that occur when using fixed-schema dictionaries. They are similar if you're just thinking about "I have a fixed set of keys that should map to a fixed set of typed values." But the resulting implementations and constraints are very different. Are a bag and a box similar? Maybe. Maybe not. Depends on your perspective and how you want to use them. Pour wine and let the discussion begin!

NamedTuple, by the way, is now a formal part of Python.

from typing import NamedTuple

class Employee(NamedTuple):
    name: str
    id: int

TypedDict started life as an experimental Mypy feature to wrangle typing onto the heterogeneous, structure-oriented use of dictionaries. As of Python 3.8, however, it was adopted into the standard library.

try:
    from typing import TypedDict  # >=3.8
except ImportError:
    from mypy_extensions import TypedDict  # <=3.7

Movie = TypedDict('Movie', {'name': str, 'year': int})

A class-based type constructor is also available:

class Movie(TypedDict):
    name: str
    year: int

Despite their differences, both NamedTuple and TypedDict lock down the specific keys to be used, and the types of values corresponding to each key. Therefore they are aiming at basically the same goal: Being useful typing mechanisms for composite/struct types.

Python's standard typing.Dict focuses on much more homogenous, parallel mappings, defining key/value types, not keys per se. Therefore it is not very useful in defining composite objects that happen to be stored in dictionaries.

ConnectionOptions = Dict[str, str] 
Backlog answered 21/11, 2018 at 10:57 Comment(6)
Thank you for the writeup. Did you deliberately go for that TypedDict syntax? Because there's also the class based syntax which make them look exactly like a the NamedTuple (from a syntax point of view)Hothouse
I used the syntax used by the Mypy documentation. Docs usually best source for what's considered canonical / preferred.Backlog
Update: TypedDict is now part of the standard library starting at Python 3.8! docs.python.org/3/library/typing.html#typing.TypedDictGauss
@KevinLanguasco Thanks for the update. Revised answer to accomodate.Backlog
I think mainly the similarities were discussed, but the differences were not.Vasiliki
I need clarification, should I pour the wine out of a bag or a box? Or was I pouring the wine into the bag/box?Itch
J
26

There are a couple of minor differences. Note that those containers haven't been there forever:

I would go for NamedTuple if possible and if I want the values to be frozen. Otherwise I would use a dataclass.

from dataclasses import dataclass
from typing import NamedTuple, TypedDict
from enum import Enum


class Gender(Enum):
    MALE = "male"
    FEMALE = "female"


## Class definition: Almost the same
@dataclass
class UserDataC:
    name: str
    gender: Gender


class UserTuple(NamedTuple):
    name: str
    gender: Gender


class UserNDict(TypedDict):
    name: str
    gender: Gender


## Object Creation: Looks the same
anna_datac = UserDataC(name="Anna", gender=Gender.FEMALE)
anna_tuple = UserTuple(name="Anna", gender=Gender.FEMALE)
anna_ndict = UserNDict(name="Anna", gender=Gender.FEMALE)

## Mutable values vs frozen values
anna_datac.gender = Gender.MALE
# anna_tuple.gender = Gender.MALE  # AttributeError: can't set attribute
anna_ndict["gender"] = Gender.MALE
# AttributeError: 'dict' object has no attribute 'gender'
# anna_ndict.gender = Gender.MALE

## New attribute
# Note that you can add new attributes like this.
# Python will not complain. But mypy will.
anna_datac.password = "secret"  # Dataclasses are extensible
# anna_tuple.password = "secret"  # AttributeError - named tuples not
# anna_ndict.password = "secret"  # AttributeError - TypedDict not
anna_ndict["password"] = "secret"

## isinstance
assert isinstance(anna_tuple, tuple)
assert isinstance(anna_ndict, dict)

Why I prefer NamedTuple over namedtuple

I think it's more intuitive to write and read. Plus you give mypy more possibilities to check:

class UserTuple(NamedTuple):
    name: str
    gender: Gender

# vs

UserTuple = namedtuple("UserTuple", ["name", "gender"])

Why I prefer tuples over dictionaries

If I don't need things to be mutable, I like if they are not. This way I prevent unexpected side effects

Johnsiejohnson answered 2/8, 2020 at 16:42 Comment(1)
And if you want to use dataclass only, you can specify immutable dataclass with @dataclass(frozen=True)Pullman
P
9

A TypedDict (in 3.8+) is

A simple typed namespace. At runtime it is equivalent to a plain dict.

whereas a NamedTuple is a "tuple subclass." Note that

Named tuple instances do not have per-instance dictionaries, so they are lightweight and require no more memory than regular tuples.

and (from here)

NamedTuple subclasses can also have docstrings and methods

To put that in my own words, a NamedTuple is more like a custom object, and a TypedDict is more like, well, a typed dictionary.

I haven't checked, but from these descriptions, I would expect NamedTuples to have some (small) runtime and memory advantages over TypedDicts.

However, if you are using an API, for example, that expects a dict, a TypedDict may be preferable since it is a dict (though you can also create a dict from a NamedTuple via its _asdict() method).

Porthole answered 3/1, 2020 at 13:53 Comment(0)
T
5

From an excellent book "Python Object-Oriented Programming" by Steven F. Lott and Dusty Phillips:

  1. For a lot of cases, dataclasses offer a number of helpful features with less code writing. They can be immutable, or mutable, giving us a wide range of options.
  2. For cases where the data is immutable, a NamedTuple can be slightly more efficient than a frozen dataclass by about 5% – not much. What tips the balance here is an expensive attribute computation. While a NamedTuple can have properties, if the computation is very costly and the results are used frequently, it can help to compute it in advance, something a NamedTuple isn't good at. Check out the documentation for dataclasses and their __post_init__() method as a better choice in the rare case where it's helpful to compute an attribute value in advance.
  3. Dictionaries are ideal when the complete set of keys isn't known in advance. When we're starting a design, we may have throwaway prototypes or proofs of concept using dictionaries. When we try to write unit tests and type hints, we may need to ramp up the formality. In some cases, the domain of possible keys is known, and a TypedDict type hint makes sense as a way to characterize the valid keys and value types.
Turkic answered 11/10, 2021 at 11:45 Comment(0)
W
3

The NamedTuple is a specific type. As the name suggests it is a tuple that is extended to have named entries.

TypedDict is not a real object, you can't (or at least shouldn't) use it, it is instead used for adding type information (for mypy type checker) to annotate types in scenarios when dictionary has various keys with different types i.e. essentially all places when one should use NamedTuple. It's very helpful to annotate existing code that you don't want to refactor.

Wanids answered 5/12, 2018 at 4:11 Comment(0)
B
0

Other differences possibly worth mentioning:

  • NamedTuples can be used as tuples, e.g. fun(*userTuple), a, b = userTuple
  • TypedDict can be used as dicts, e.g. fun(**userDict)

Behaviours above are also in contrast to dataclass-es, where fields typically need to be referenced explicitly - if not resorting to dataclasses.astuple(), .asdict().

Similarities:

  • TypedDict can only inherit from other TypedDicts [1], and NamedTuple can only inherit from other NamedTuples
  • both support generics, with recent Python versions [2][3]
Bullish answered 28/12, 2022 at 12:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.