named tuple
default values
optional arguments
keyword arguments
Python

Named tuple and default values for optional keyword arguments

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Python's namedtuple from the collections module creates lightweight, immutable tuple subclasses with named fields. Adding default values for optional fields requires specific techniques because namedtuple does not natively support defaults in Python versions before 3.6.1. Since Python 3.6.1, namedtuple accepts a defaults parameter. For more flexibility, typing.NamedTuple and dataclasses provide cleaner syntax for defaults. This article covers all approaches.

Basic NamedTuple

python
1from collections import namedtuple
2
3Point = namedtuple("Point", ["x", "y"])
4p = Point(1, 2)
5print(p.x, p.y)  # 1 2
6print(p)          # Point(x=1, y=2)
7
8# Immutable — cannot modify fields
9# p.x = 10  # AttributeError

Without defaults, all fields are required. Point() or Point(1) raises TypeError.

Defaults with defaults Parameter (Python 3.6.1+)

python
1from collections import namedtuple
2
3# defaults apply to the rightmost fields
4Config = namedtuple("Config", ["host", "port", "timeout", "retries"],
5                    defaults=["localhost", 8080, 30, 3])
6
7# All defaults
8c1 = Config()
9print(c1)  # Config(host='localhost', port=8080, timeout=30, retries=3)
10
11# Override some
12c2 = Config("example.com", 443)
13print(c2)  # Config(host='example.com', port=443, timeout=30, retries=3)
14
15# Keyword arguments
16c3 = Config(host="db.local", retries=5)
17print(c3)  # Config(host='db.local', port=8080, timeout=30, retries=5)

defaults applies from right to left. If there are 4 fields and 2 defaults, the last 2 fields get defaults while the first 2 remain required.

python
1# 4 fields, 2 defaults — first 2 required, last 2 optional
2Request = namedtuple("Request", ["method", "url", "headers", "body"],
3                     defaults=[{}, None])
4
5r = Request("GET", "/api/users")
6print(r)  # Request(method='GET', url='/api/users', headers={}, body=None)

Using typing.NamedTuple (Python 3.6+)

Class-based syntax with type hints and defaults:

python
1from typing import NamedTuple
2
3class Config(NamedTuple):
4    host: str
5    port: int = 8080
6    timeout: int = 30
7    retries: int = 3
8
9c = Config("example.com")
10print(c)  # Config(host='example.com', port=8080, timeout=30, retries=3)
11
12# Type hints are checked by mypy/pyright
13# c = Config(host=123)  # Type error (int instead of str)

Fields without defaults must come before fields with defaults, just like function arguments.

Pre-3.6 Workaround: __new__.__defaults__

python
1from collections import namedtuple
2
3Config = namedtuple("Config", ["host", "port", "timeout"])
4Config.__new__.__defaults__ = ("localhost", 8080, 30)
5
6c = Config()
7print(c)  # Config(host='localhost', port=8080, timeout=30)

This sets default values by modifying the __defaults__ tuple on the __new__ method. It works in Python 2 and early Python 3.

Using _replace for Updating

Since namedtuples are immutable, use _replace to create modified copies:

python
1from typing import NamedTuple
2
3class User(NamedTuple):
4    name: str
5    email: str
6    role: str = "viewer"
7    active: bool = True
8
9user = User("Alice", "[email protected]")
10print(user)  # User(name='Alice', email='[email protected]', role='viewer', active=True)
11
12# Create a modified copy
13admin = user._replace(role="admin")
14print(admin)  # User(name='Alice', email='[email protected]', role='admin', active=True)
15
16# Original unchanged
17print(user.role)  # viewer

Comparison with Dataclasses

python
1from dataclasses import dataclass
2
3@dataclass
4class Config:
5    host: str
6    port: int = 8080
7    timeout: int = 30
8    retries: int = 3
9
10c = Config("example.com")
11print(c)  # Config(host='example.com', port=8080, timeout=30, retries=3)
12
13# Mutable — fields can be changed
14c.port = 443
15
16# Supports post-init processing, field factories, etc.
Featurenamedtupletyping.NamedTupledataclass
ImmutableYesYesNo (unless frozen=True)
DefaultsYes (3.6.1+)YesYes
Type hintsNoYesYes
Tuple unpackingYesYesNo
_replace methodYesYesNo (use replace() 3.13+)
Custom methodsNoLimitedYes
Memory efficiencyBestBestGood

Converting Between Formats

python
1from typing import NamedTuple
2
3class Point(NamedTuple):
4    x: float
5    y: float
6    label: str = ""
7
8p = Point(1.5, 2.5, "A")
9
10# To dictionary
11d = p._asdict()
12print(d)  # {'x': 1.5, 'y': 2.5, 'label': 'A'}
13
14# From dictionary
15p2 = Point(**d)
16print(p2)  # Point(x=1.5, y=2.5, label='A')
17
18# Unpack like a tuple
19x, y, label = p
20print(x, y)  # 1.5 2.5

Common Pitfalls

  • Putting required fields after fields with defaults: Like function parameters, required namedtuple fields must come before optional ones. class Config(NamedTuple): port: int = 8080; host: str raises TypeError because host (no default) follows port (has default).
  • Using mutable default values: defaults=[[], {}] shares the same mutable object across all instances. Unlike dataclass field(default_factory=list), namedtuple defaults are not copied per instance. Use immutable defaults (tuples, frozensets, None) and create mutable objects after construction.
  • Confusing defaults count with field count: If you have 4 fields and pass defaults=[1, 2], the defaults apply to the last 2 fields, not the first 2. Fields without defaults remain required.
  • Modifying namedtuple fields directly: Namedtuples are immutable. point.x = 10 raises AttributeError. Use _replace(x=10) to create a new instance with the modified value.
  • Using namedtuple when you need mutability: If fields need to change after creation, use a dataclass instead. Converting between namedtuple and dataclass later requires changing all call sites that depend on tuple unpacking or indexing.

Summary

  • Use namedtuple("Name", fields, defaults=[...]) for defaults in Python 3.6.1+
  • Use typing.NamedTuple with class syntax for type hints and inline defaults
  • Defaults apply right-to-left — required fields must come first
  • Use _replace() to create modified copies (namedtuples are immutable)
  • Use dataclass instead when you need mutability, custom methods, or __post_init__
  • Avoid mutable default values ([], {}) — they are shared across instances

Course illustration
Course illustration

All Rights Reserved.