A Python library for easy access and manipulation of nested data structures.
Project description
Nested
A Python library for easy access and manipulation of nested data structures.
nested treats Python objects as trees: containers (dict, list, tuple,
namedtuple, dataclass, attrs) form the nodes and everything else forms
the leaves. It provides a concise, dot-separated field-path syntax
(e.g. 'a.1.bar') for accessing, checking, updating, adding, removing,
and comparing elements at arbitrary depth — without writing chains of
brackets and attribute lookups by hand.
Installation
pip install gdm-nested
Quick Start
import collections
import nested
MyTuple = collections.namedtuple('MyTuple', ['foo', 'bar'])
obj = {
'a': [1, (2, 3, 4)],
'b': MyTuple(foo='f', bar='b'),
}
# Access deeply nested values with a dot-separated path.
nested.access_field(obj, 'a.0') # → 1
nested.access_field(obj, 'a.1.2') # → 4
nested.access_field(obj, 'b.bar') # → 'b'
# Check if a path exists.
nested.check_field(obj, 'a.0') # → True
nested.check_field(obj, 'a.99') # → False
# Update (or upsert) a value.
nested.update_field(obj, 'b.foo', 'g')
# → {'a': [1, (2, 3, 4)], 'b': MyTuple(foo='g', bar='b')}
# Remove a field and get the popped value.
obj2, popped = nested.remove_field(obj, 'b')
# obj2 → {'a': [1, (2, 3, 4)]}
# popped → MyTuple(foo='f', bar='b')
Supported Container Types
nested recognises the following types as tree nodes:
| Type | Description |
|---|---|
dict |
Any dict subclass, collections.OrderedDict, collections.defaultdict, ml_collections.ConfigDict |
list |
Any list instance |
tuple |
Plain tuples (not namedtuples) |
namedtuple |
Any tuple-like class with a _fields attribute |
dataclass |
Any dataclasses.dataclass instance |
attrs |
Any attr.s / attrs.define instance |
Everything else is treated as an opaque leaf.
Note: Sequences and custom containers are only recognised as structures when derived from
dictorlist.
Field Path Syntax
Fields are specified as dot-separated strings, tuples, or single keys:
# All of these are equivalent:
nested.access_field(obj, 'a.1.2')
nested.access_field(obj, ('a', 1, 2))
nested.access_field(obj, ('a', '1', '2'))
- String keys address dict keys and namedtuple/dataclass/attrs field names.
- Integer keys address list and tuple indices (including negative indices).
Comparison with dm-tree
At first glance, nested might seem similar to
google-deepmind/tree, as both operate
on nested Python structures (dictionaries, lists, tuples). However, they solve
entirely different problems and are highly complementary.
treeis for Transformation: It focuses on applying operations across the entirety of a nested structure. You usetreewhen you want to map a function over every leaf node (tree.map_structure), flatten a complex structure into a 1D list (tree.flatten), or verify that two structures have the same exact shape.nestedis for Navigation and Access: It focuses on traversal and targeting.treelacks utilities to easily grab a specific value deep inside a structure without manual indexing.nestedprovides the tools to safely and cleanly query, extract, and navigate to specific paths within complex, heavily-nested objects.
Rule of thumb:
If you need to multiply every number in a nested dictionary by 2, use tree.
If you need to safely extract a specific configuration value buried five levels
deep in that dictionary, use nested.
API Reference
Accessing Fields
access_field(nested_obj, field)
Returns the value at the given field path.
nested.access_field({'x': [10, 20]}, 'x.1') # → 20
access_fields(nested_obj, fields)
Returns a list of values for multiple field paths.
nested.access_fields({'x': 1, 'y': 2}, ['x', 'y']) # → [1, 2]
get(nested_obj, field, value=None)
Like access_field, but returns a default value instead of raising when the
field does not exist.
nested.get({'x': 1}, 'y', 'default') # → 'default'
Checking Fields
check_field(nested_obj, field)
Returns True if the field path exists.
nested.check_field({'x': [1]}, 'x.0') # → True
nested.check_field({'x': [1]}, 'x.5') # → False
check_fields(nested_obj, fields)
Returns a list of booleans for multiple field paths.
all(nested.check_fields(obj, ['a', 'b'])) # True if both exist
Updating Fields
update_field(nested_obj, field, value)
Replaces the value if the field exists, or inserts it if it doesn't (upsert).
nested.update_field({'x': 1}, 'x', 2) # → {'x': 2}
nested.update_field({'x': 1}, 'y', 2) # → {'x': 1, 'y': 2}
update_fields(nested_obj, *field_value_tuples, **field_value_kwargs)
Batch update/upsert multiple fields at once.
nested.update_fields({'a': 1}, ('b', 2), c=3) # → {'a': 1, 'b': 2, 'c': 3}
Replacing Fields
replace_field(nested_obj, field, value)
Like update_field, but raises KeyError if the field does not already
exist.
nested.replace_field({'x': 1}, 'x', 2) # → {'x': 2}
nested.replace_field({'x': 1}, 'y', 2)
# raises KeyError: '"y" field not in structure.'
nested.replace_field({'x': {'z': 1}}, 'x.y', 2)
# raises KeyError: '"y" field not in structure.'
replace_fields(nested_obj, *field_value_tuples, **field_value_kwargs)
Batch version of replace_field.
Adding Fields
add_field(nested_obj, field, value)
Like update_field, but raises KeyError if the field already exists.
nested.add_field({'x': 1}, 'y', 2) # → {'x': 1, 'y': 2}
# raises KeyError: '"x" field already in structure.'
nested.add_field({'x': 1}, 'x', 2)
add_fields(nested_obj, *field_value_tuples, **field_value_kwargs)
Batch version of add_field.
Note: For sequences, elements are inserted at the given index. Order matters — additions are applied sequentially.
Removing Fields
remove_field(nested_obj, field, *, default=...)
Removes a field and returns a (modified_obj, popped_value) tuple. Pass
default to suppress errors when the field is missing.
obj, val = nested.remove_field({'x': 1, 'y': 2}, 'x') # obj → {'y': 2}, val → 1
obj, val = nested.remove_field({'x': 1, 'y': 2}, 'z', default=3)
# obj → {'y': 2}, val → 3
remove_fields(nested_obj, *fields, default=...)
Removes multiple fields sequentially, returning a
(modified_obj, [popped_values]) tuple.
Note: Removals are applied sequentially, so indices may shift for list-based structures.
Introspection
field_names(nested_obj, root='')
Returns a structure parallel to nested_obj where each leaf is replaced by
its dot-separated path string.
nested.field_names({'a': [1, 2], 'b': 3}) # → {'a': ['a.0', 'a.1'], 'b': 'b'}
Comparison
assert_equal(a, b, custom_is_equals=...)
Raises ValueError if two nested structures are not structurally and
value-equal. NumPy arrays are compared element-wise by default. Custom
comparators can be supplied via custom_is_equals.
nested.assert_equal({'x': [1, 2]}, {'x': [1, 2]}) # passes
nested.assert_equal({'x': {'y':1}}, {'x': {'y':2}})
# ValueError: The two nested structure are not equal at path: "x.y".
nested.assert_equal({'x': {'y':1}}, {'x': {'z':2}})
# ValueError: The two structures don't have the same nested structure.
# First structure: type=dict str={'x': {'y': None}}
# Second structure: type=dict str={'x': {'z': None}}
Contributing
See CONTRIBUTING.md for details.
Licence and Disclaimer
Copyright 2026 Google LLC
All software is licensed under the Apache License, Version 2.0 (Apache 2.0); you may not use this file except in compliance with the Apache 2.0 license. You may obtain a copy of the Apache 2.0 license at: https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, all software and materials distributed here under the Apache 2.0 are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the licenses for the specific language governing permissions and limitations under those licenses.
This is not an official Google product.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gdm_nested-1.0.0.tar.gz.
File metadata
- Download URL: gdm_nested-1.0.0.tar.gz
- Upload date:
- Size: 25.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d29c032cb8dc2c0a78b7319e92fc0df50d5ff4f5d0f4f0729171f6ffd6df93a5
|
|
| MD5 |
3da9dc1a0d9c32313d4a6a2768206047
|
|
| BLAKE2b-256 |
b023c0ddf03f6bd5bab2f6aca6fe216f5d4c8f4dd2a74154719b6147066dc18c
|
File details
Details for the file gdm_nested-1.0.0-py3-none-any.whl.
File metadata
- Download URL: gdm_nested-1.0.0-py3-none-any.whl
- Upload date:
- Size: 21.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6929c4bf8321bfa5afb8405b9017056385377d4be9a37add63497c7227164894
|
|
| MD5 |
bdfd607abd968aff91f232198240eecc
|
|
| BLAKE2b-256 |
b9995c6f98cffae668f5d05d9f080edfdd74c238a0fd686e4ac0633105c81e9b
|