271

Consider following piece of code:

from collections import namedtuple
point = namedtuple("Point", ("x:int", "y:int"))

The Code above is just a way to demonstrate as to what I am trying to achieve. I would like to make namedtuple with type hints.

Do you know any elegant way how to achieve result as intended?

1

3 Answers 3

362

The preferred syntax for a typed namedtuple since Python 3.6 is using typing.NamedTuple like so:

from typing import NamedTuple

class Point(NamedTuple):
    x: int
    y: int = 1  # Set default value

Point(3)  # -> Point(x=3, y=1)

Starting with Python 3.7, consider using a dataclasses:

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int = 1  # Set default value

Point(3)  # -> Point(x=3, y=1)
Sign up to request clarification or add additional context in comments.

7 Comments

@JohnE; The OP specifically asked for named tuples. Yes, many use cases of named tuples will be better served by data classes. But to quote the excellent Why not namedtuples: If you want a tuple with names, by all means: go for a namedtuple
Using dataclasses, it is not possible to deconstruct the resulting object like you could a Tuple
A tuple is immutable. A dataclass is not (by default) It does have the frozen flag which gets close to tuple's behaviour. Just something to be aware of.
if dataclass works for you, you can go further and use pydantic package to enforce type checking on runtime in elegant way.
Dataclasses aren't subscribable, and they neither are unpackable while iterating as named tuples do so I think they are far from being a perfect alternative.
|
189

You can use typing.NamedTuple

From the docs

Typed version of namedtuple.

>>> import typing
>>> Point = typing.NamedTuple("Point", [('x', int), ('y', int)])

This is present only in Python 3.5 onwards

3 Comments

In newer versions you may declare NamedTuples as Point = typing.NamedTuple("Point", x=int, y=int), which is much cleaner and shorter.
^ Documentation, discussion, and examples floating around for this syntax is nearly non-existent. I only found it in the cpython typing.py file. It states the syntax is available from 3.6+, and it's at least in the 3.7+ code. Though there doesn't seem to be a cleaner version of this for setting defaults adjacent to the member/type declaration.
^^ While the keyword-argument syntax mentioned by @MarkedasDuplicate still works (tested on Python 3.10.9 (tags/v3.10.9:1dd9be6)), it was removed from the docstrings in pull 105287 because according to the pull author, "this way of creating NamedTuples isn't supported by any [known] type checker".
22

Just to be fair, NamedTuple from typing:

>>> from typing import NamedTuple
>>> class Point(NamedTuple):
...     x: int
...     y: int = 1  # Set default value
...
>>> Point(3)
Point(x=3, y=1)

equals to classic namedtuple:

>>> from collections import namedtuple
>>> p = namedtuple('Point', 'x,y', defaults=(1, ))
>>> p.__annotations__ = {'x': int, 'y': int}
>>> p(3)
Point(x=3, y=1)

So, NamedTuple is just syntax sugar for namedtuple

Below, you can find a creating NamedTuple function from the source code of python 3.10. As we can see, it uses collections.namedtuple constructor and adds __annotations__ from extracted types:

def _make_nmtuple(name, types, module, defaults = ()):
    fields = [n for n, t in types]
    types = {n: _type_check(t, f"field {n} annotation must be a type")
             for n, t in types}
    nm_tpl = collections.namedtuple(name, fields,
                                    defaults=defaults, module=module)
    nm_tpl.__annotations__ = nm_tpl.__new__.__annotations__ = types
    return nm_tpl

3 Comments

Syntactic sugar is something the parser can replace with more fundamental syntax. NamedTuple is a bit more complicated than that, being a function that actually does something at runtime.
Yes, I know what it does do during runtime. It is extracting types and adds them to __annotations__ attr of just created namedtuple using constructor collections.namedtuple. I added that code to the answer for better understanding.
@mrvol Thanks for this. it really helped me understand what is actually happening, and that the resulting object is the same in both cases.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.