What's the difference between namedtuple and NamedTuple?

python, python-3.x

Solution

The type generated by subclassing `typing.NamedTuple` is equivalent to a `collections.namedtuple`, but with `__annotations__`, `_field_types` and `_field_defaults` attributes added. The generated code will behave the same, for all practical purposes, since nothing in Python currently acts on those typing related attributes (your IDE might use them, though).

As a developer, using the `typing` module for your namedtuples allows a more natural declarative interface:

- You can easily specify default values for the fields (edit: in Python 3.7, `collections.namedtuple` got a new `defaults` keyword so this is no longer an advantage)

- You don't need to repeat the type name twice ("Employee")

- You can customize the type directly (e.g. adding a docstring or some methods)

As before, your class will be a subclass of `tuple`, and instances will be instances of `tuple` as usual. Interestingly, your class will not be a subclass of `NamedTuple`. If you want to know why, read on for more info about the implementation detail.

from typing import NamedTuple

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

Behaviour in Python <= 3.8

>>> issubclass(Employee, NamedTuple)
False
>>> isinstance(Employee(name='guido', id=1), NamedTuple)
False

`typing.NamedTuple` is a class, it uses metaclasses and a custom `__new__` to handle the annotations, and then it delegates to `collections.namedtuple` to build and return the type. As you may have guessed from the lowercased name convention, `collections.namedtuple` is not a type/class - it's a factory function. It works by building up a string of Python source code, and then calling `exec` on this string. The generated constructor is plucked out of a namespace and included in a 3-argument invocation of the metaclass `type` to build and return your class. This explains the weird inheritance breakage seen above, `NamedTuple` uses a metaclass in order to use a different metaclass to instantiate the class object.

Behaviour in Python >= 3.9

`typing.NamedTuple` is changed from a type (`class`) to a function (`def`)

>>> issubclass(Employee, NamedTuple)
TypeError: issubclass() arg 2 must be a class or tuple of classes
>>> isinstance(Employee(name="guido", id=1), NamedTuple)
TypeError: isinstance() arg 2 must be a type or tuple of types

The metaclass acrobatics are gone, now it's just a simple factory function which calls `collections.namedtuple` and then sets `__annotations__` on the returned type. Multiple inheritance using `NamedTuple` is now disallowed (it did not work properly in the first place).

See bpo40185 / GH-19371 for the change.

Problem

The `typing` module documentation says that the two code snippets below are equivalent. ``` from typing import NamedTuple class Employee(NamedTuple): name: str id: int ``` and ``` from collections import namedtuple Employee = namedtuple('Employee', ['name', 'id']) ``` Are they the exact same thing or, if not, what are the differences between the two implementations?

Original source