Utilities¶
Multi-level dictionary¶
dictionary.Dictionary
is an expansion of the standard dict class which supports tree structures (nested dictionaries). The extensions include:
- handling multi-level paths and nested dictionaries, with several different indexing methods
- iteration over the immediate branches, or over the whole tree structure
- some additional methods: mapping, filtering, finding difference between two dictionaries
- combined with
pylablib.core.fileio
allows to save and load the content in a human-readable format.
Creating and indexing:
>>> d = pll.Dictionary()
>>> d['d/0/x'] = 5
>>> d
Dictionary('d/0/x': 5)
>>> d['d/0/x'] # string path indexing
5
>>> d['d']['0']['x'] # nested indexing
5
>>> d['d','0','x'] # multi-level path indexing
5
>>> d['d',0,'x'] # all path elements are converted into strings
5
>>> d['d/0']['x'] # indexing styles can be freely mixed
5
>>> d['d','0/x']
5
>>> b = d['d'] # indexing a branch yields another Dictionary object
>>> b
Dictionary('0/x': 5)
>>> b['0/x'] = 10 # the branch shares the data with the main dictionary
>>> d
Dictionary('d/0/x': 10)
A dictionary can be build from a Python dict
, which automatically normalizes paths and nested dictionaries:
>>> d = pll.Dictionary({ 'a':1, 'b/i':2, 'c':{'i':3, 'ii':4}, 'd/0/x':5 })
>>> d
Dictionary('b/i': 2
'c/i': 3
'c/ii': 4
'd/0/x': 5
'a': 1)
Note
There are several limitations on the dictionary structure (mostly they involve possible paths and keys):
- As mentioned above, the keys are converted into strings to get the path; therefore, different Python object can merge together (e.g., number
0
and string literal'0'
). This also discourages use of some of the objects with “underdefined” (implementation dependent) representations, for example, floating point numbers.- Since the
'/'
symbol is used to split different path entries, it can’t be used inside a single-level key. It is possible to re-define this symbol on dictionary creation; however, it might lead to compatibility issues.- Using spaces is in principle allowed; however, it leads to problems if the dictionary is saved to or loaded from a text file using standard methods, since there space is used to separate the path and the value (so a part of the path after the first space would become a part of the value). The same concerns other whitespace characters (
'\n'
,'\r'
,'\t'
).- Empty keys are not allowed. When building a path, they are automatically dropped, so
'a/b'
,'a/b/'
,'a///b//'
all correspond to the same path.- One path can either correspond to a branch node, or a leaf node. In other words, one path can’t be a prefix of other paths and also contain data: structures like
pll.Dictionary({ 'a':1, 'a/b':2})
are not allowed. To get around this, one can define a specific “data key” not used anywhere else, and store data in a node under that key (e.g., with the data key'#'
the example before turns into a valid structurepll.Dictionary({ 'a/#':1, 'a/b/#':2})
).
Thus, it is generally recommended to only use strings or non-negative integers as keys, and apply the same restrictions to them as to the Python variable names (with the addition of names starting with a digit).