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:
_StackedDictNested 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:
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_pathsGet compact representation of paths
PathsViewDocumentation 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:
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
pathsGet standard paths view
CompactPathsViewDocumentation 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
dfsDepth-first traversal
unpacked_itemsGet 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
dfsDepth-first traversal
_HKey.bfsTree-based BFS traversal
- copy() _StackedDict¶
Create a shallow copy of the _StackedDict.
- Returns:
Shallow copy with same configuration
- Return type:
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
deepcopyCreate 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:
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
copyShallow 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.setterset 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
bfsBreadth-first traversal
unpacked_itemsSimilar 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
- classmethod from_dict(dictionary: dict[Any, Any], **class_options) _StackedDict¶
Recursively convert a standard dictionary to a
_StackedDictor subclass.Alternative constructor that transforms a regular nested dictionary into a
_StackedDict-based structure. The target class isclsitself, 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_setupkey.
- Returns:
New instance of
clscontaining the dictionary structure.- Return type:
- Raises:
StackedKeyError – If
default_setupis missing fromclass_options.
Examples
>>> nd = NestedDictionary.from_dict( ... {'a': {'b': 1}}, ... default_setup={'indent': 0, 'default_factory': None} ... ) >>> nd['a']['b'] 1
Notes
Already-instantiated
_StackedDictvalues are preserved as-is.Regular
dictvalues are recursively converted usingcls.Non-dict values are assigned directly.
See also
to_dictInverse 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__:valuetagged strings are decoded back to their original Python types. Round-trips are lossless for supported types:str,int,float,bool, flattuple, flatfrozenset.- Parameters:
path (str or Path) – Path to the JSON file.
**class_options (dict) – Passed to
cls.from_dict; must includedefault_setup.
- Returns:
Reconstructed instance of
cls.- Return type:
Examples
>>> nd = NestedDictionary.from_json( ... '/tmp/nd.json', ... default_setup={'indent': 0, 'default_factory': None} ... )
See also
to_jsonSerialize 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:
- Raises:
StackedValueError – If
verify=Trueand the digest mismatches or sidecar is absent.- Warns:
UserWarning – Pickle is unsafe with untrusted files.
See also
to_pickleSerialize 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
- 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
heightGet maximum depth
_HKey.is_balancedTree-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
occurrencesCount how many times key appears
key_listGet 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.
- 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_listGet paths containing the key
unpacked_itemsGet 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_keyCheck if key exists
items_listGet values at paths containing key
occurrencesCount 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_valuesGenerator alternative
dfsTraversal 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
- 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
popitemRemove 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
popRemove item by key
unpacked_itemsView 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
- 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
heightGet 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_dictConvert 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 key42→"__int__:42", tuple key(1, 2)→"__tuple__:(1, 2)"). Round-trips are lossless for supported types:str,int,float,bool, flattuple, flatfrozenset. File I/O is delegated tojson.dump.- Parameters:
path (str or Path) – Destination file path.
indent (int, optional) – JSON indentation level. Defaults to
self.indentif not provided.
Examples
>>> nd = NestedDictionary({'a': {'b': 1}}) >>> nd.to_json('/tmp/nd.json')
See also
from_jsonReconstruct 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_pickleReconstruct 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_keysGet only the paths
unpacked_valuesGet only the values
dfsDepth-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_itemsGet (path, value) pairs
pathsGet 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_itemsGet (path, value) pairs
leavesAlternative 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_dictConvert dict to _StackedDict
- indent: int¶
indent is used to print the dictionary with json indentation
- class ndict_tools.StrictNestedDictionary(*args, **kwargs)¶
Bases:
NestedDictionaryStrict 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: aKeyErroris raised instead of silently creating a new nested dictionary.
- class ndict_tools.SmoothNestedDictionary(*args, **kwargs)¶
Bases:
NestedDictionarySmooth 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 emptySmoothNestedDictionary— even at arbitrary depth.
Path views¶
- class ndict_tools.PathsView(stacked_dict: _StackedDict | None = None)¶
Bases:
_PathsA 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
_Pathsclass 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
CompactPathsViewCompact representation of paths
NestedDictionaryNested dictionary with path operations
- to_compact() CompactPathsView¶
Convert this PathsView to a CompactPathsView.
- Returns:
Compact representation with the same paths
- Return type:
Examples
>>> paths = nd.paths() >>> compact = paths.to_compact() >>> compact.structure [['a', 'b', 'c'], ['d']]
- class ndict_tools.CompactPathsView(stacked_dict: _StackedDict | None = None)¶
Bases:
_CPathsA 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
PathsViewStandard view for iterating over paths
NestedDictionaryNested dictionary with path operations
expand_structureStatic 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.