Dictionary and Path Classes

This module provides tools and class for creating nested dictionaries, since standard python does not have nested dictionaries.

Dictionary classes

class ndict_tools.NestedDictionary(*args, **kwargs)

Bases: _StackedDict

Nested dictionary class.

This class is designed as a stacked dictionary. It represents a nest of dictionaries, that is to say that each key is a value or a nested dictionary. And so on…

Parameters:
  • *args (Iterable) – The first one of the list must be a dictionary to instantiate an object

  • **kwargs (dict) –

    Enrichments settings:

    • indentint, optional

      Indentation of the printable nested dictionary (used by json.dumps() function)

    • strictbool, optional (default=False)

      Strict mode define default answer to unknown key

    • default_setupdict, optional

      Custom setup for default behavior

Examples

>>> NestedDictionary({'first': 1,'second': {'1': "2:1", '2': "2:2", '3': "3:2"}, 'third': 3, 'fourth': 4})
>>> NestedDictionary(zip(['first','second', 'third', 'fourth'],
...                  [1, {'1': "2:1", '2': "2:2", '3': "3:2"}, 3, 4]))
>>> NestedDictionary([('first', 1), ('second', {'1': "2:1", '2': "2:2", '3': "3:2"}),
...                   ('third', 3), ('fourth', 4)])

Initialize a new _StackedDict with configuration and optional data.

The constructor requires configuration via the ‘default_setup’ parameter, which must contain at least ‘indent’ and ‘default_factory’ keys. Additional initialization data can be provided through args or kwargs.

Parameters:
  • *args (iterable) – Dictionaries, _StackedDict instances, or iterables of (key, value) pairs

  • **kwargs (dict) – Either: - ‘default_setup’: dict with ‘indent’ and ‘default_factory’ keys - Direct key-value pairs to initialize (requires default_setup in kwargs)

Raises:
  • StackedKeyError – If ‘indent’ or ‘default_factory’ is missing from configuration

  • StackedAttributeError – If default_setup contains keys that aren’t valid attributes

Examples

>>> setup = {'indent': 2, 'default_factory': None}
>>> sd = _StackedDict(default_setup=setup)
>>> # Initialize with data
>>> sd = _StackedDict({'a': 1}, default_setup=setup)
>>> # Copy another _StackedDict
>>> sd2 = _StackedDict(sd, default_setup=setup)

Notes

  • Args are processed sequentially, later values override earlier ones

  • _StackedDict args are deep-copied

  • Regular dicts are converted to _StackedDict recursively

  • All configuration is propagated to nested instances

paths() PathsView

Get a view of all hierarchical paths in this dictionary.

Returns a lazy view over all paths without storing them in memory. The view supports iteration, length queries, membership tests, and various path operations.

Returns:

A lazy view over all paths in the nested dictionary

Return type:

PathsView

Examples

>>> nd = NestedDictionary({'a': {'b': 1, 'c': 2}, 'd': 3})
>>> paths = nd.paths()
>>> list(paths)
[['a'], ['a', 'b'], ['a', 'c'], ['d']]
>>> # Check if path exists
>>> ['a', 'b'] in paths
True
>>> # Get number of paths
>>> len(paths)
4
>>> # Get children of a path
>>> paths.get_children(['a'])
['b', 'c']

See also

compact_paths

Get compact representation of paths

PathsView

Documentation of the paths view class

compact_paths() CompactPathsView

Get a compact representation of all paths in this dictionary.

Returns a compact view where the hierarchical structure is represented as nested lists, providing a factorized representation of paths.

Returns:

A compact view with factorized path structure

Return type:

CompactPathsView

Examples

>>> nd = NestedDictionary({'a': {'b': 1, 'c': 2}, 'd': 3})
>>> cpaths = nd.compact_paths()
>>> cpaths.structure
[['a', 'b', 'c'], ['d']]
>>> # Expand to full paths
>>> cpaths.expand()
[['a'], ['a', 'b'], ['a', 'c'], ['d']]
>>> # Check coverage
>>> cpaths.is_covering(nd)
True

See also

paths

Get standard paths view

CompactPathsView

Documentation of the compact paths view class

ancestors(value)

Find the hierarchical path (ancestors) leading to a specific value.

Searches the nested structure for the given value and returns the complete path of keys leading to it, excluding the final key. This returns the “ancestry” of the value in the tree.

Parameters:

value (Any) – Value to search for in the nested dictionary

Returns:

list of keys forming the path to the value (excluding final key)

Return type:

list

Raises:

StackedValueError – If value is not found in the dictionary

Examples

>>> sd = _StackedDict({'a': {'b': {'c': 1}}}, default_setup={'indent': 2, 'default_factory': None})
>>> sd.ancestors(1)
['a', 'b']
>>> sd = _StackedDict({'a': {'b': 1}, 'c': {'d': 2}}, default_setup={'indent': 2, 'default_factory': None})
>>> sd.ancestors(2)
['c']
>>> sd.ancestors(999)  # Raises StackedValueError

Notes

  • Returns path excluding the final key (direct parent of value)

  • Uses DFS to find the first occurrence

  • If value appears multiple times, returns first found path

  • Empty list returned for top-level values

See also

dfs

Depth-first traversal

unpacked_items

Get all (path, value) pairs

bfs() Generator[tuple[tuple[Any, ...], Any], None, None]

Breadth-First Search traversal of the nested dictionary.

Iteratively traverses the dictionary level by level, visiting all nodes at depth N before moving to depth N+1. Uses a queue (deque) for efficient FIFO operations.

Yields:

tuple – (path_tuple, value) for each node in BFS order

Examples

>>> sd = _StackedDict({'a': {'b': {'c': 1}}}, default_setup={'indent': 2, 'default_factory': None})
>>> for path, value in sd.bfs():
...     print(f'{path} -> {value}')
('a',) -> <_StackedDict>
('a', 'b') -> <_StackedDict>
('a', 'b', 'c') -> 1
>>> # Only leaf values
>>> sd = _StackedDict({'a': {'b': 1, 'c': 2}, 'd': 3}, default_setup={'indent': 2, 'default_factory': None})
>>> leaves = [(p, v) for p, v in sd.bfs() if not isinstance(v, _StackedDict)]
>>> leaves
[(('a', 'b'), 1), ('a', 'c'), 2), (('d',), 3)]

Notes

  • Visits all nodes at same depth before going deeper

  • Returns paths as immutable tuples

  • Includes all nodes (intermediate and terminal)

  • More memory efficient than collecting all paths first

See also

dfs

Depth-first traversal

_HKey.bfs

Tree-based BFS traversal

copy() _StackedDict

Create a shallow copy of the _StackedDict.

Returns:

Shallow copy with same configuration

Return type:

_StackedDict

Examples

>>> sd = _StackedDict({'a': {'b': 1}}, default_setup={'indent': 2, 'default_factory': None})
>>> sd2 = sd.copy()
>>> sd2['a'] is sd['a']
True

See also

__copy__

Internal implementation

deepcopy

Create independent copy

deepcopy() _StackedDict

Create a deep copy of the _StackedDict.

Creates a completely independent copy where all nested structures are recursively duplicated.

Returns:

Complete independent copy

Return type:

_StackedDict

Examples

>>> sd = _StackedDict({'a': {'b': 1}}, default_setup={'indent': 2, 'default_factory': None})
>>> sd2 = sd.deepcopy()
>>> sd2['a']['b'] = 999
>>> sd['a']['b']
1

See also

__deepcopy__

Internal implementation

copy

Shallow copy alternative

property default_setup: list[tuple[str, Any]]

Get configuration as an ordered list of (key, value) tuples.

Returns a deterministic view of the internal configuration with priority ordering: ‘indent’, ‘default_factory’, then alphabetically.

Returns:

Configuration as [(key, value), …] in priority order

Return type:

list of tuple

Examples

>>> sd = _StackedDict(default_setup={'indent': 2, 'default_factory': None})
>>> sd.default_setup
[('indent', 2), ('default_factory', None)]

See also

default_setup.setter

set new configuration

dfs(node=None, path=None) Generator[tuple[list[Any], Any], None, None]

Depth-First Search traversal of the nested dictionary.

Recursively traverses the dictionary in depth-first order, yielding each hierarchical path (as list) and its corresponding value. This includes both intermediate nodes and leaf values.

Parameters:
  • node (dict, optional) – Current node being traversed (defaults to root/self)

  • path (list, optional) – Current path being constructed (defaults to empty list)

Yields:

tuple – (path_list, value) for each node in DFS order

Examples

>>> sd = _StackedDict({'a': {'b': 1}, 'c': 2}, default_setup={'indent': 2, 'default_factory': None})
>>> for path, value in sd.dfs():
...     print(f'{path} -> {value}')
['a'] -> <_StackedDict>
['a', 'b'] -> 1
['c'] -> 2

Notes

  • Visits nodes before their children (pre-order)

  • Returns paths as mutable lists (unlike unpacked_keys)

  • Includes intermediate dictionary nodes

  • Useful for tree-based operations

See also

bfs

Breadth-first traversal

unpacked_items

Similar but only terminal values

equal(other)

Check equality: same class, configuration, and content.

Two _StackedDict instances are equal if they have: 1. Identical class type (exact match, not subclasses) 2. Identical default_setup configuration 3. Identical dictionary structure and values

Parameters:

other (Any) – Object to compare with

Returns:

True if all conditions met

Return type:

bool

Examples

>>> setup = {'indent': 2, 'default_factory': None}
>>> sd1 = _StackedDict({'a': 1}, default_setup=setup)
>>> sd2 = _StackedDict({'a': 1}, default_setup=setup)
>>> sd1 == sd2
True
>>> # Different setup
>>> sd3 = _StackedDict({'a': 1}, default_setup={'indent': 4, 'default_factory': None})
>>> sd1 == sd3
False

See also

__eq__

dictionaries equalities

__ne__

Inequality check

similar

Compare content only (ignore class/setup)

isomorph

Compare as plain dicts

classmethod from_dict(dictionary: dict[Any, Any], **class_options) _StackedDict

Recursively convert a standard dictionary to a _StackedDict or subclass.

Alternative constructor that transforms a regular nested dictionary into a _StackedDict-based structure. The target class is cls itself, eliminating the need to pass the class explicitly and preventing errors in recursive calls.

Parameters:
  • dictionary (dict) – The dictionary to transform (may be nested).

  • **class_options (dict) – Initialization options. Must contain default_setup key.

Returns:

New instance of cls containing the dictionary structure.

Return type:

_StackedDict

Raises:

StackedKeyError – If default_setup is missing from class_options.

Examples

>>> nd = NestedDictionary.from_dict(
...     {'a': {'b': 1}},
...     default_setup={'indent': 0, 'default_factory': None}
... )
>>> nd['a']['b']
1

Notes

  • Already-instantiated _StackedDict values are preserved as-is.

  • Regular dict values are recursively converted using cls.

  • Non-dict values are assigned directly.

See also

to_dict

Inverse operation.

classmethod from_json(path: str | Path, **class_options) _StackedDict

Reconstruct a _StackedDict (or subclass) from a JSON file.

Non-string keys stored as __type__:value tagged strings are decoded back to their original Python types. Round-trips are lossless for supported types: str, int, float, bool, flat tuple, flat frozenset.

Parameters:
  • path (str or Path) – Path to the JSON file.

  • **class_options (dict) – Passed to cls.from_dict; must include default_setup.

Returns:

Reconstructed instance of cls.

Return type:

_StackedDict

Examples

>>> nd = NestedDictionary.from_json(
...     '/tmp/nd.json',
...     default_setup={'indent': 0, 'default_factory': None}
... )

See also

to_json

Serialize to a JSON file.

classmethod from_pickle(path: str | Path, verify: bool = True, **class_options) _StackedDict

Reconstruct a _StackedDict (or subclass) from a pickle file.

Parameters:
  • path (str or Path) – Path to the pickle file.

  • verify (bool, optional) – If True (default), verify the SHA-256 sidecar before loading.

  • **class_options (dict) – Not used directly (the pickled object carries its own state), but accepted for API symmetry with from_json.

Returns:

Reconstructed instance.

Return type:

_StackedDict

Raises:

StackedValueError – If verify=True and the digest mismatches or sidecar is absent.

Warns:

UserWarning – Pickle is unsafe with untrusted files.

See also

to_pickle

Serialize to a pickle file.

height() int

Compute the height (maximum depth) of the nested structure.

The height is defined as the length of the longest path from root to any leaf node. An empty dictionary has height 0.

Returns:

Maximum path length in the dictionary

Return type:

int

Examples

>>> sd = _StackedDict({'a': 1}, default_setup={'indent': 2, 'default_factory': None})
>>> sd.height()
1
>>> sd = _StackedDict({'a': {'b': {'c': 1}}}, default_setup={'indent': 2, 'default_factory': None})
>>> sd.height()
3
>>> sd = _StackedDict(default_setup={'indent': 2, 'default_factory': None})
>>> sd.height()
0

Notes

  • Empty dictionary has height 0

  • Single-level dictionary has height 1

  • O(n) complexity where n is number of paths

See also

size

Count total number of keys

leaves

Get all leaf values

is_balanced() bool

Check if the nested structure is height-balanced.

A balanced dictionary is one where the height difference between any two subtrees at the same level differs by at most 1. This indicates a relatively uniform distribution of nesting depth.

Returns:

True if structure is balanced, False otherwise

Return type:

bool

Examples

>>> # Balanced
>>> sd = _StackedDict({'a': {'b': 1}, 'c': {'d': 2}}, default_setup={'indent': 2, 'default_factory': None})
>>> sd.is_balanced()
True
>>> # Unbalanced
>>> sd = _StackedDict({'a': {'b': {'c': 1}}, 'd': 2}, default_setup={'indent': 2, 'default_factory': None})
>>> sd.is_balanced()
False

Notes

  • Uses recursive height calculation

  • Checks balance at every node

  • Empty dictionary is considered balanced

  • Useful for identifying skewed structures

See also

height

Get maximum depth

_HKey.is_balanced

Tree-based balance check

is_key(key: Any) bool

Check if an atomic key exists at any level in the hierarchy.

Searches through all hierarchical paths to determine if the given atomic key appears anywhere in the nested structure. Does not accept hierarchical paths (lists).

Parameters:

key (Any) – Atomic key to search for (not a list)

Returns:

True if key exists at any level

Return type:

bool

Raises:

StackedKeyError – If key is a list (hierarchical keys not allowed)

Examples

>>> sd = _StackedDict({'a': {'b': 1}}, default_setup={'indent': 2, 'default_factory': None})
>>> sd.is_key('b')
True
>>> sd.is_key('c')
False
>>> # Lists not allowed
>>> sd.is_key(['a', 'b'])  # Raises StackedKeyError

Notes

  • Only searches for atomic keys

  • Checks all nesting levels

  • O(n) complexity where n is total number of keys

See also

occurrences

Count how many times key appears

key_list

Get all paths containing the key

isomorph(other)

Check if structures are isomorphic (same keys/values, any dict type).

Two structures are isomorphic if they represent the same nested dictionary structure, regardless of whether they’re _StackedDict, plain dict, or any other dict-like type.

Parameters:

other (dict or _StackedDict) – Dictionary to compare with

Returns:

True if structure-preserving mapping exists

Return type:

bool

Examples

>>> sd = _StackedDict({'a': {'b': 1}}, default_setup={'indent': 2, 'default_factory': None})
>>> regular_dict = {'a': {'b': 1}}
>>> sd.isomorph(regular_dict)
True
>>> sd.isomorph({'a': {'b': 2}})
False

Notes

Checks if sd[k1]…[kn] == other[k1]…[kn] for all paths. This is the most permissive comparison method.

See also

__eq__

dictionaries equalities

__ne__

Inequality check

equal

Strict equality

similar

Compare _StackedDict instances

items_list(key: Any) list[Any]

Get all values associated with paths containing a specific key.

Returns a list of all terminal values whose hierarchical paths contain the specified atomic key at any position.

Parameters:

key (Any) – Atomic key to search for

Returns:

list of values from paths containing the key

Return type:

list

Raises:

StackedKeyError – If key doesn’t exist in the dictionary

Examples

>>> sd = _StackedDict({'a': {'b': 1}, 'c': {'b': 2}}, default_setup={'indent': 2, 'default_factory': None})
>>> sd.items_list('b')
[1, 2]
>>> sd.items_list('a')
[1]
>>> sd.items_list('nonexistent')  # Raises StackedKeyError

Notes

  • Returns values in the order they’re encountered during traversal

  • Multiple values returned if key appears in multiple paths

  • Only returns terminal (leaf) values

See also

key_list

Get paths containing the key

unpacked_items

Get all (path, value) pairs

key_list(key: Any) list[list[Any]]

Get all hierarchical paths containing a specific key.

Returns a list of all complete paths (as tuples) that contain the specified atomic key at any position in the path.

Parameters:

key (Any) – Atomic key to search for

Returns:

list of paths (as tuples) containing the key

Return type:

list

Raises:

StackedKeyError – If key doesn’t exist in the dictionary

Examples

>>> sd = _StackedDict({'a': {'b': 1}, 'c': {'b': 2}}, default_setup={'indent': 2, 'default_factory': None})
>>> sd.key_list('b')
[('a', 'b'), ('c', 'b')]
>>> sd.key_list('a')
[('a', 'b')]
>>> sd.key_list('nonexistent')  # Raises StackedKeyError

See also

is_key

Check if key exists

items_list

Get values at paths containing key

occurrences

Count occurrences

leaves() list[Any]

Extract all leaf (terminal) values from the nested structure.

Returns a list of all values that are not themselves nested dictionaries, i.e., all terminal nodes in the tree structure.

Returns:

list of all leaf values

Return type:

list

Examples

>>> sd = _StackedDict({'a': {'b': 1}, 'c': 2}, default_setup={'indent': 2, 'default_factory': None})
>>> sd.leaves()
[1, 2]
>>> sd = _StackedDict({'a': {'b': {'c': 1}}}, default_setup={'indent': 2, 'default_factory': None})
>>> sd.leaves()
[1]
>>> # Empty dict as leaf value
>>> sd = _StackedDict({'a': {}}, default_setup={'indent': 2, 'default_factory': None})
>>> sd.leaves()
[{}]

Notes

  • Returns values in DFS order

  • Empty dictionaries are considered leaf values

  • Plain dicts (not _StackedDict) are also leaves

See also

unpacked_values

Generator alternative

dfs

Traversal including intermediate nodes

occurrences(key: Any) int

Count occurrences of an atomic key throughout the hierarchy.

Returns the total number of times a key appears in the nested structure, counting each occurrence in every hierarchical path.

Parameters:

key (Any) – Atomic key to count

Returns:

Number of occurrences (0 if key doesn’t exist)

Return type:

int

Examples

>>> sd = _StackedDict({'a': {'b': 1}, 'c': {'b': 2}}, default_setup={'indent': 2, 'default_factory': None})
>>> sd.occurrences('b')
2
>>> sd.occurrences('a')
1
>>> sd.occurrences('z')
0
>>> # Key appearing multiple times in same path
>>> sd = _StackedDict({'a': {'a': 1}}, default_setup={'indent': 2, 'default_factory': None})
>>> sd.occurrences('a')
2

See also

is_key

Check if key exists

key_list

Get all paths containing the key

pop(key: Any | list[Any], default=None) Any

Remove and return value at key or hierarchical path.

Removes the specified key (flat or hierarchical) and returns its value. Automatically cleans up empty parent dictionaries after removal. If the key doesn’t exist, returns the default value or raises an error.

Parameters:
  • key (Any or list[Any]) – Single key or hierarchical path to remove

  • default (Any, optional) – Value to return if key doesn’t exist

Returns:

The value that was removed

Return type:

Any

Raises:

StackedKeyError – If key doesn’t exist and no default provided

Examples

>>> sd = _StackedDict({'a': {'b': 1}, 'c': 2}, default_setup={'indent': 2, 'default_factory': None})
>>> sd.pop('c')
2
>>> 'c' in sd
False
>>> # Hierarchical key
>>> sd.pop(['a', 'b'])
1
>>> 'a' in sd  # Empty 'a' was removed
False
>>> # With default
>>> sd.pop('nonexistent', 'default_value')
'default_value'

See also

popitem

Remove and return last item

__delitem__

Delete without returning value

popitem()

Remove and return the last item as (path, value) pair.

Removes the last item in the most deeply nested dictionary, returning its full hierarchical path and value. Uses depth-first traversal to locate the deepest rightmost item.

Returns:

(path_list, value) where path_list is the hierarchical path

Return type:

tuple

Raises:

StackedIndexError – If the dictionary is empty

Examples

>>> sd = _StackedDict({'a': {'b': 1, 'c': 2}}, default_setup={'indent': 2, 'default_factory': None})
>>> path, value = sd.popitem()
>>> path
['a', 'c']
>>> value
2
>>> # Empty dictionary
>>> sd = _StackedDict(default_setup={'indent': 2, 'default_factory': None})
>>> sd.popitem()  # Raises StackedIndexError

Notes

  • Follows DFS to find the last (rightmost, deepest) item

  • Cleans up empty parent dictionaries automatically

  • Path is returned as a list of keys

See also

pop

Remove item by key

unpacked_items

View all (path, value) pairs

similar(other)

Check if two structures share the same content (ignoring setup).

Two structures are similar if they: 1. Are both _StackedDict instances (any subclass) 2. Have identical dictionary content (keys and values)

Configuration differences are ignored.

Parameters:

other (Any) – Object to compare with

Returns:

True if both are _StackedDict with same content

Return type:

bool

Examples

>>> setup1 = {'indent': 2, 'default_factory': None}
>>> setup2 = {'indent': 4, 'default_factory': _StackedDict}
>>> sd1 = _StackedDict({'a': 1}, default_setup=setup1)
>>> sd2 = _StackedDict({'a': 1}, default_setup=setup2)
>>> sd1 == sd2
False
>>> sd1.similar(sd2)
True

See also

__eq__

dictionaries equalities

__ne__

Inequality check

equal

Strict equality (includes setup)

isomorph

Compare as plain dicts

size() int

Compute the total number of keys (nodes) in the structure.

Counts all keys at all nesting levels, including both intermediate dictionary keys and terminal value keys.

Returns:

Total number of keys across all levels

Return type:

int

Examples

>>> sd = _StackedDict({'a': {'b': 1}}, default_setup={'indent': 2, 'default_factory': None})
>>> sd.size()
2
>>> sd = _StackedDict({'a': {'b': 1, 'c': 2}, 'd': 3}, default_setup={'indent': 2, 'default_factory': None})
>>> sd.size()
4

Notes

  • Counts all keys at all levels

  • Equivalent to number of nodes in tree representation

  • Different from len() which only counts top-level keys

See also

height

Get maximum depth

__len__

Get top-level key count

to_dict() dict[Any, Any]

Convert to a standard nested dictionary.

Recursively converts the _StackedDict and all nested _StackedDict instances to regular Python dictionaries, removing all special functionality but preserving the structure.

Returns:

Regular nested dictionary with same structure

Return type:

dict

Examples

>>> sd = _StackedDict({'a': {'b': 1}}, default_setup={'indent': 2, 'default_factory': None})
>>> regular = sd.to_dict()
>>> type(regular)
<class 'dict'>
>>> regular
{'a': {'b': 1}}

Notes

  • All _StackedDict instances are converted recursively

  • Other value types are preserved as-is

  • Inverse operation of from_dict()

See also

from_dict

Convert dict to _StackedDict

__deepcopy__

Create _StackedDict copy

to_json(path: str | Path, indent: int | None = None) None

Serialize this dictionary to a JSON file.

Non-string keys are encoded as type-tagged strings of the form __type__:value (e.g., integer key 42"__int__:42", tuple key (1, 2)"__tuple__:(1, 2)"). Round-trips are lossless for supported types: str, int, float, bool, flat tuple, flat frozenset. File I/O is delegated to json.dump.

Parameters:
  • path (str or Path) – Destination file path.

  • indent (int, optional) – JSON indentation level. Defaults to self.indent if not provided.

Examples

>>> nd = NestedDictionary({'a': {'b': 1}})
>>> nd.to_json('/tmp/nd.json')

See also

from_json

Reconstruct from a JSON file.

to_pickle(path: str | Path, protocol: int | None = None) None

Serialize this dictionary to a pickle file with SHA-256 verification.

Writes two files: <path> (pickle) and <path>.sha256 (hex digest).

Parameters:
  • path (str or Path) – Destination file path.

  • protocol (int, optional) – Pickle protocol. Defaults to pickle.DEFAULT_PROTOCOL.

Warns:

UserWarning – Pickle is unsafe with untrusted files.

See also

from_pickle

Reconstruct from a pickle file.

unpacked_items() Generator[tuple[tuple[Any, ...], Any], None, None]

Generate all (path, value) pairs from nested structure.

Yields terminal values along with their hierarchical paths as tuples. This provides a flattened view of the entire nested dictionary.

Yields:

tuple – (path_tuple, value) for each terminal value

Examples

>>> sd = _StackedDict({'a': {'b': 1}, 'c': 2}, default_setup={'indent': 2, 'default_factory': None})
>>> list(sd.unpacked_items())
[(('a', 'b'), 1), (('c',), 2)]
>>> # Empty dict as value
>>> sd = _StackedDict({'a': {}}, default_setup={'indent': 2, 'default_factory': None})
>>> list(sd.unpacked_items())
[(('a',), {})]

Notes

  • Uses depth-first traversal

  • Empty dictionaries are treated as terminal values

  • Paths are immutable tuples for hashability

See also

unpacked_keys

Get only the paths

unpacked_values

Get only the values

dfs

Depth-first traversal alternative returning lists

unpacked_keys() Generator[tuple[Any, ...], None, None]

Generate all hierarchical paths (keys) from nested structure.

Yields the path to each terminal value as a tuple of keys, providing access to all navigable paths in the dictionary.

Yields:

tuple – Path as tuple of keys

Examples

>>> sd = _StackedDict({'a': {'b': 1}}, default_setup={'indent': 2, 'default_factory': None})
>>> list(sd.unpacked_keys())
[('a', 'b')]
>>> sd = _StackedDict({'a': {'b': 1, 'c': 2}, 'd': 3}, default_setup={'indent': 2, 'default_factory': None})
>>> sorted(sd.unpacked_keys())
[('a', 'b'), ('a', 'c'), ('d',)]

See also

unpacked_items

Get (path, value) pairs

paths

Get paths as _Paths view object

unpacked_values() Generator[Any, None, None]

Generate all terminal values from nested structure.

Yields only the leaf values, discarding path information. Useful for collecting all data values regardless of structure.

Yields:

Any – Each leaf value in the nested dictionary

Examples

>>> sd = _StackedDict({'a': {'b': 1}, 'c': 2}, default_setup={'indent': 2, 'default_factory': None})
>>> list(sd.unpacked_values())
[1, 2]
>>> # Empty dict is a value
>>> sd = _StackedDict({'a': {}, 'b': 1}, default_setup={'indent': 2, 'default_factory': None})
>>> list(sd.unpacked_values())
[{}, 1]

See also

unpacked_items

Get (path, value) pairs

leaves

Alternative method returning list

update(_StackedDict__m: Mapping[Any, Any] | Iterable[tuple[Any, Any]] | None = None, **kwargs) None

Update _StackedDict with key/value pairs from mapping, iterable, or kwargs.

Merges the provided mapping, iterable of key-value pairs, or keyword arguments into this _StackedDict, converting regular dicts to _StackedDict instances recursively while preserving existing _StackedDict values.

Parameters:
  • __m (Mapping[Any, Any] or Iterable[tuple[Any, Any]], optional) – Either a mapping (dict, _StackedDict) or an iterable of (key, value) tuples to merge. If None, only kwargs are used.

  • **kwargs (Any) – Additional key/value pairs to merge

Examples

>>> sd = _StackedDict({'a': 1}, default_setup={'indent': 2, 'default_factory': None})
>>> # From dict
>>> sd.update({'b': 2, 'c': {'d': 3}})
>>> sd['c']['d']
3
>>> # From iterable
>>> sd.update([('e', 4), ('f', {'g': 5})])
>>> sd['f']['g']
5
>>> # Using kwargs
>>> sd.update(h=6, i={'j': 7})
>>> sd['i']['j']
7
>>> # Combined
>>> sd.update({'k': 8}, l=9)
>>> sd['k'], sd['l']
(8, 9)

Notes

  • Accepts mappings (dict, _StackedDict, etc.)

  • Accepts iterables of (key, value) tuples

  • Accepts keyword arguments

  • Regular dicts are converted to _StackedDict recursively

  • _StackedDict values are accepted directly with synchronized config

  • Configuration is synchronized across all nested instances

  • Later values override earlier ones for duplicate keys

See also

__init__

Initialization with data

__setitem__

set individual items

from_dict

Convert dict to _StackedDict

indent: int

indent is used to print the dictionary with json indentation

class ndict_tools.StrictNestedDictionary(*args, **kwargs)

Bases: NestedDictionary

Strict nested dictionary class.

This class is designed to implement a non-default answer to an unknown key.

Parameters:
  • *args (Iterable) – Positional arguments passed to NestedDictionary

  • **kwargs (dict) – Keyword arguments passed to NestedDictionary

Notes

This class overwrites the default_factory attribute to None, preventing automatic creation of nested dictionaries for unknown keys.

Initialize a new _StackedDict with configuration and optional data.

The constructor requires configuration via the ‘default_setup’ parameter, which must contain at least ‘indent’ and ‘default_factory’ keys. Additional initialization data can be provided through args or kwargs.

Parameters:
  • *args (iterable) – Dictionaries, _StackedDict instances, or iterables of (key, value) pairs

  • **kwargs (dict) – Either: - ‘default_setup’: dict with ‘indent’ and ‘default_factory’ keys - Direct key-value pairs to initialize (requires default_setup in kwargs)

Raises:
  • StackedKeyError – If ‘indent’ or ‘default_factory’ is missing from configuration

  • StackedAttributeError – If default_setup contains keys that aren’t valid attributes

Examples

>>> setup = {'indent': 2, 'default_factory': None}
>>> sd = _StackedDict(default_setup=setup)
>>> # Initialize with data
>>> sd = _StackedDict({'a': 1}, default_setup=setup)
>>> # Copy another _StackedDict
>>> sd2 = _StackedDict(sd, default_setup=setup)

Notes

  • Args are processed sequentially, later values override earlier ones

  • _StackedDict args are deep-copied

  • Regular dicts are converted to _StackedDict recursively

  • All configuration is propagated to nested instances

All methods are inherited from NestedDictionary. The only difference is the behaviour on unknown keys: a KeyError is raised instead of silently creating a new nested dictionary.

class ndict_tools.SmoothNestedDictionary(*args, **kwargs)

Bases: NestedDictionary

Smooth nested dictionary class.

This class is designed to implement a default answer as an empty SmoothNestedDictionary to an unknown key.

Parameters:
  • *args (Iterable) – Positional arguments passed to NestedDictionary

  • **kwargs (dict) – Keyword arguments passed to NestedDictionary

Notes

This class overwrites the default_factory attribute to SmoothNestedDictionary, automatically creating nested dictionaries for unknown keys.

Initialize a new _StackedDict with configuration and optional data.

The constructor requires configuration via the ‘default_setup’ parameter, which must contain at least ‘indent’ and ‘default_factory’ keys. Additional initialization data can be provided through args or kwargs.

Parameters:
  • *args (iterable) – Dictionaries, _StackedDict instances, or iterables of (key, value) pairs

  • **kwargs (dict) – Either: - ‘default_setup’: dict with ‘indent’ and ‘default_factory’ keys - Direct key-value pairs to initialize (requires default_setup in kwargs)

Raises:
  • StackedKeyError – If ‘indent’ or ‘default_factory’ is missing from configuration

  • StackedAttributeError – If default_setup contains keys that aren’t valid attributes

Examples

>>> setup = {'indent': 2, 'default_factory': None}
>>> sd = _StackedDict(default_setup=setup)
>>> # Initialize with data
>>> sd = _StackedDict({'a': 1}, default_setup=setup)
>>> # Copy another _StackedDict
>>> sd2 = _StackedDict(sd, default_setup=setup)

Notes

  • Args are processed sequentially, later values override earlier ones

  • _StackedDict args are deep-copied

  • Regular dicts are converted to _StackedDict recursively

  • All configuration is propagated to nested instances

All methods are inherited from NestedDictionary. The only difference is that every access to an unknown key returns a new empty SmoothNestedDictionary — even at arbitrary depth.

Path views

class ndict_tools.PathsView(stacked_dict: _StackedDict | None = None)

Bases: _Paths

A view providing access to all hierarchical paths in a nested dictionary.

Similar to the standard dict.keys() view, but designed specifically for hierarchical paths in nested dictionaries. Provides lazy iteration over all paths without storing them in memory.

This is the public API for working with paths. It inherits all functionality from the internal _Paths class and ensures that conversions return public class instances.

Parameters:

stacked_dict (NestedDictionary or _StackedDict) – The nested dictionary to create a view for

Examples

>>> nd = NestedDictionary({'a': {'b': 1, 'c': 2}, 'd': 3})
>>> paths = nd.paths()
>>> type(paths).__name__
'PathsView'
>>> # Iterate over paths
>>> for path in paths:
...     print(path)
['a']
['a', 'b']
['a', 'c']
['d']
>>> # Check if path exists
>>> ['a', 'b'] in paths
True
>>> # Get number of paths
>>> len(paths)
4
>>> # Get children of a path
>>> paths.get_children(['a'])
['b', 'c']
>>> # Get leaf paths only
>>> paths.get_leaf_paths()
[['a', 'b'], ['a', 'c'], ['d']]
>>> # Convert to compact representation
>>> compact = paths.to_compact()
>>> type(compact).__name__
'CompactPathsView'

See also

CompactPathsView

Compact representation of paths

NestedDictionary

Nested dictionary with path operations

to_compact() CompactPathsView

Convert this PathsView to a CompactPathsView.

Returns:

Compact representation with the same paths

Return type:

CompactPathsView

Examples

>>> paths = nd.paths()
>>> compact = paths.to_compact()
>>> compact.structure
[['a', 'b', 'c'], ['d']]
class ndict_tools.CompactPathsView(stacked_dict: _StackedDict | None = None)

Bases: _CPaths

A view providing compact representation of hierarchical paths.

Provides a factorized/compact representation where the hierarchical structure is represented as nested lists. This is useful for efficiently representing and manipulating path structures, especially when dealing with large numbers of similar paths.

The compact structure uses nested lists where:

  • Leaf nodes are represented by their key alone

  • Internal nodes are represented as [key, child1, child2, …]

This class provides a bijective mapping between compact and expanded forms, allowing efficient conversion in both directions.

Parameters:

stacked_dict (NestedDictionary or _StackedDict) – The nested dictionary to create a compact view for

structure

The compact representation as nested lists (lazy-built, read/write)

Type:

List[Any]

Examples

>>> nd = NestedDictionary({'a': {'b': 1, 'c': 2}, 'd': 3})
>>> cpaths = nd.compact_paths()
>>> type(cpaths).__name__
'CompactPathsView'
>>> # Get compact structure
>>> cpaths.structure
[['a', 'b', 'c'], ['d']]
>>> # Expand to full paths
>>> cpaths.expand()
[['a'], ['a', 'b'], ['a', 'c'], ['d']]
>>> # Can still iterate (inherited from PathsView)
>>> list(cpaths)
[['a'], ['a', 'b'], ['a', 'c'], ['d']]
>>> # Set custom structure
>>> cpaths.structure = [['x', 'y', 'z']]
>>> cpaths.expand()
[['x'], ['x', 'y'], ['x', 'z']]
>>> # Check coverage against original dictionary
>>> cpaths = nd.compact_paths()
>>> cpaths.is_covering(nd)
True
>>> cpaths.coverage(nd)
1.0
>>> # Partial structure
>>> cpaths.structure = [['a', 'b']]
>>> cpaths.coverage(nd)
0.5
>>> cpaths.uncovered_paths(nd)
[['a', 'c'], ['d']]

Notes

Compact structure format:

  • Simple leaf: 'key'

  • Node with children: ['key', child1, child2, ...]

Examples of compact structures:

  • [['a'], ['b']] → two independent paths: ['a'] and ['b']

  • [['a', 'b', 'c']] → paths: ['a'], ['a', 'b'], ['a', 'c']

  • [['a', ['b', 'c']]] → equivalent to the above (explicit nesting)

See also

PathsView

Standard view for iterating over paths

NestedDictionary

Nested dictionary with path operations

expand_structure

Static method to expand a compact structure

Methods inherited from PathsView (iteration, membership, filtering…) are documented on that class. Only the members specific to compact representation are listed here.

to_paths() PathsView

Convert this CompactPathsView to a PathsView.

Returns:

Standard paths view with the same underlying data

Return type:

PathsView

Examples

>>> cpaths = nd.compact_paths()
>>> paths = cpaths.to_paths()
>>> type(paths).__name__
'PathsView'