Introduction
Python's namedtuple has a built-in _asdict() method that converts it to an OrderedDict (Python 3.7 and earlier) or a regular dict (Python 3.8+). For nested namedtuples, you need recursive conversion. The dataclasses.asdict() function handles this automatically for dataclasses, which are the modern alternative to namedtuples for structured data.
Basic Conversion with _asdict()
1from collections import namedtuple
2
3Point = namedtuple("Point", ["x", "y"])
4p = Point(10, 20)
5
6# Convert to dictionary
7d = p._asdict()
8print(d) # {'x': 10, 'y': 20}
9print(type(d)) # <class 'dict'> (Python 3.8+)
10
11# Access like a normal dict
12print(d["x"]) # 10
13print(d.keys()) # dict_keys(['x', 'y'])
The _asdict() method is part of the namedtuple API (the underscore prefix does not mean it is private — namedtuples use this convention to avoid name conflicts with field names).
Using dict() Constructor
1from collections import namedtuple
2
3Color = namedtuple("Color", ["red", "green", "blue"])
4c = Color(255, 128, 0)
5
6# Alternative: use dict() on _asdict()
7d = dict(c._asdict())
8
9# Or use the ** unpacking with zip
10d = dict(zip(c._fields, c))
11print(d) # {'red': 255, 'green': 128, 'blue': 0}
Nested Namedtuples
_asdict() only converts the top level — nested namedtuples remain as namedtuples:
1from collections import namedtuple
2
3Address = namedtuple("Address", ["street", "city", "state"])
4Person = namedtuple("Person", ["name", "age", "address"])
5
6addr = Address("123 Main St", "NYC", "NY")
7person = Person("Alice", 30, addr)
8
9# Shallow conversion — address is still a namedtuple
10d = person._asdict()
11print(d)
12# {'name': 'Alice', 'age': 30, 'address': Address(street='123 Main St', city='NYC', state='NY')}
13print(type(d["address"])) # <class '__main__.Address'>
14
15# Deep conversion — recursive function
16def namedtuple_to_dict(obj):
17 if hasattr(obj, "_asdict"):
18 return {k: namedtuple_to_dict(v) for k, v in obj._asdict().items()}
19 elif isinstance(obj, (list, tuple)):
20 return [namedtuple_to_dict(item) for item in obj]
21 return obj
22
23d = namedtuple_to_dict(person)
24print(d)
25# {'name': 'Alice', 'age': 30, 'address': {'street': '123 Main St', 'city': 'NYC', 'state': 'NY'}}
Converting Back: Dict to Namedtuple
1from collections import namedtuple
2
3Point = namedtuple("Point", ["x", "y"])
4
5# From dict to namedtuple
6d = {"x": 10, "y": 20}
7p = Point(**d)
8print(p) # Point(x=10, y=20)
9
10# From dict with extra keys — filter to valid fields
11data = {"x": 10, "y": 20, "z": 30, "color": "red"}
12p = Point(**{k: v for k, v in data.items() if k in Point._fields})
13print(p) # Point(x=10, y=20)
JSON Serialization
1import json
2from collections import namedtuple
3
4Config = namedtuple("Config", ["host", "port", "debug"])
5config = Config("localhost", 8080, True)
6
7# Serialize to JSON via dict
8json_str = json.dumps(config._asdict())
9print(json_str) # {"host": "localhost", "port": 8080, "debug": true}
10
11# Deserialize from JSON back to namedtuple
12data = json.loads(json_str)
13config2 = Config(**data)
14print(config2) # Config(host='localhost', port=8080, debug=True)
Modern Alternative: dataclasses
1from dataclasses import dataclass, asdict, fields
2
3@dataclass
4class Point:
5 x: float
6 y: float
7
8@dataclass
9class Line:
10 start: Point
11 end: Point
12
13line = Line(Point(0, 0), Point(10, 20))
14
15# asdict() handles nested dataclasses automatically
16d = asdict(line)
17print(d) # {'start': {'x': 0, 'y': 0}, 'end': {'x': 10, 'y': 20}}
18
19# Unlike namedtuple, dataclasses are mutable
20line.start.x = 5
typing.NamedTuple (Python 3.6+)
1from typing import NamedTuple
2
3class Employee(NamedTuple):
4 name: str
5 department: str
6 salary: float
7
8emp = Employee("Alice", "Engineering", 120000)
9
10# _asdict() works the same way
11d = emp._asdict()
12print(d) # {'name': 'Alice', 'department': 'Engineering', 'salary': 120000}
13
14# Type hints are preserved
15print(Employee.__annotations__)
16# {'name': <class 'str'>, 'department': <class 'str'>, 'salary': <class 'float'>}
1from collections import namedtuple
2from dataclasses import dataclass, asdict
3import timeit
4
5NTPoint = namedtuple("NTPoint", ["x", "y"])
6
7@dataclass
8class DCPoint:
9 x: float
10 y: float
11
12nt = NTPoint(1.0, 2.0)
13dc = DCPoint(1.0, 2.0)
14
15timeit.timeit(lambda: nt._asdict(), number=1_000_000) # ~0.25s
16timeit.timeit(lambda: asdict(dc), number=1_000_000) # ~1.1s
17timeit.timeit(lambda: {"x": dc.x, "y": dc.y}, number=1_000_000) # ~0.15s
_asdict() is faster than dataclasses.asdict() because it does less work (no recursive copying). Manual dict construction is fastest of all.
Common Pitfalls
Expecting _asdict() to handle nested namedtuples: _asdict() only converts the top-level namedtuple to a dict. Nested namedtuples remain as namedtuple objects. Write a recursive conversion function or switch to dataclasses.asdict() which handles nesting automatically.
Treating _asdict as private: The underscore prefix in _asdict, _fields, and _replace is part of the namedtuple API, not a privacy indicator. These methods are the documented, correct way to interact with namedtuples. They use underscores to avoid conflicts with user-defined field names.
Modifying the returned dict and expecting the namedtuple to change: Namedtuples are immutable. The dict from _asdict() is a copy. Modifying d["x"] = 99 does not change the original namedtuple. Use _replace(x=99) to create a new namedtuple with modified values.
Using dict(namedtuple) directly: dict(p) does not work on namedtuples — it raises TypeError because namedtuples are not mappings. You must use p._asdict() or dict(zip(p._fields, p)). Dataclasses also require asdict(dc), not dict(dc).
Using namedtuple when you need mutability: Namedtuples are immutable — you cannot change field values after creation. If you need mutable records that convert to dicts, use @dataclass which supports both asdict() and attribute assignment.
Summary
Use namedtuple._asdict() to convert a namedtuple to a dictionary
_asdict() returns a regular dict in Python 3.8+ (OrderedDict in earlier versions)
For nested namedtuples, write a recursive converter or use dataclasses.asdict()
Convert dict back to namedtuple with MyTuple(**my_dict)
typing.NamedTuple provides the same _asdict() with added type annotations
Prefer dataclasses over namedtuple for new code — they handle nesting, mutability, and conversion better