235

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?

Alex Waygood
  • 6,304
  • 3
  • 24
  • 46
Pavel Hanpari
  • 4,029
  • 2
  • 20
  • 23
  • 5
    Also, starting with py3.7 you have the dataclass option: https://docs.python.org/3/library/dataclasses.html – JohnE Jul 23 '18 at 09:37

3 Answers3

319

The prefered Syntax for a typed named tuple since 3.6 is

from typing import NamedTuple

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

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

Edit Starting Python 3.7, consider using dataclasses (your IDE may not yet support them for static type checking):

from dataclasses import dataclass

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

Point(3)  # -> Point(x=3, y=1)
martineau
  • 119,623
  • 25
  • 170
  • 301
Wolfgang Kuehn
  • 12,206
  • 2
  • 33
  • 46
  • 36
    @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](http://www.attrs.org/en/stable/why.html#namedtuples): _If you want a tuple with names, by all means: go for a namedtuple_ – Wolfgang Kuehn Jul 30 '18 at 14:14
  • 10
    Using dataclasses, it is not possible to deconstruct the resulting object like you could a Tuple – VARAK Oct 24 '19 at 12:33
  • 33
    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. – shao.lo Dec 02 '19 at 22:22
  • 4
    if dataclass works for you, you can go further and use [pydantic](https://pydantic-docs.helpmanual.io/) package to enforce type checking on runtime in elegant way. – izkeros Feb 18 '21 at 07:42
  • 6
    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. – Vichoko Oct 18 '21 at 21:56
171

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

Stefan
  • 1,246
  • 1
  • 9
  • 13
Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
  • 12
    In newer versions you may declare NamedTuples as `Point = typing.NamedTuple("Point", x=int, y=int)`, which is much cleaner and shorter. – Marked as Duplicate Oct 11 '21 at 20:56
  • 1
    ^ Documentation, discussion, and examples floating around for this syntax is nearly non-existent. I only found it in [the cpython typing.py file](https://github.com/python/cpython/blob/d7a5aca982def155a9255893cefcc1493c127c9c/Lib/typing.py#L2408). 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. – JWCS Feb 10 '22 at 19:46
13

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
mrvol
  • 2,575
  • 18
  • 21
  • 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. – chepner Nov 17 '21 at 18:05
  • 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 Nov 17 '21 at 20:47