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?
Tuples are immutable, whether named or not. namedtuple only makes the access more convenient, by using names instead of indices. You can only use valid identifiers for namedtuple , it doesn't perform any hashing — it generates a new type instead.
In general, you can use namedtuple instances wherever you need a tuple-like object. Named tuples have the advantage that they provide a way to access their values using field names and the dot notation. This will make your code more Pythonic.
Named tuple container datatype is an alternative to the built-in tuple . This extension type enhances standard tuples so that their elements can be accessed by both their attribute name and the positional index. Named tuples are available in Python's standard library collections module under the namedtuple utility.
The NamedTuple is another class, under the collections module. Like the dictionary type objects, it contains keys and that are mapped to some values. In this case we can access the elements using keys and indexes. To use it at first we need to import it the collections standard library module. import collections.
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:
collections.namedtuple
got a new defaults
keyword so this is no longer an advantage)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
>>> 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
, anyway, 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.
typing.NamedTuple
is changed from a class
to a 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
Multiple inheritance using NamedTuple
is now disallowed (it did not work properly in the first place).
See bpo40185 / GH-19371 for the change.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With